diff --git a/0.1.1/zs-convert-to-webp.php b/0.1.1/zs-convert-to-webp.php new file mode 100644 index 0000000..f844c00 --- /dev/null +++ b/0.1.1/zs-convert-to-webp.php @@ -0,0 +1,53 @@ +

Error: Image conversion to WebP failed. The original file has been kept.

'; + }); + } + } + + return $upload; +} + + diff --git a/0.2.1/css/webp-test.css b/0.2.1/css/webp-test.css new file mode 100644 index 0000000..3cdc5eb --- /dev/null +++ b/0.2.1/css/webp-test.css @@ -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; +} diff --git a/0.2.1/js/webp-test.js b/0.2.1/js/webp-test.js new file mode 100644 index 0000000..299e49b --- /dev/null +++ b/0.2.1/js/webp-test.js @@ -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: ' + response.data.source_info + ''); + sliderContainer.show(); + setOverlayWidth(50); + } else { + alert(response.data || 'Error loading images'); + } + }); + }); + }); +}); diff --git a/0.2.1/samples/blackboard.nfo b/0.2.1/samples/blackboard.nfo new file mode 100644 index 0000000..5c5722e --- /dev/null +++ b/0.2.1/samples/blackboard.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/business-idea-planning-board-3683781/ diff --git a/0.2.1/samples/blackboard.png b/0.2.1/samples/blackboard.png new file mode 100644 index 0000000..75fcccc Binary files /dev/null and b/0.2.1/samples/blackboard.png differ diff --git a/0.2.1/samples/building.nfo b/0.2.1/samples/building.nfo new file mode 100644 index 0000000..5751ad0 --- /dev/null +++ b/0.2.1/samples/building.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/mexico-cdmx-reform-chapultepec-7596566/ diff --git a/0.2.1/samples/building.png b/0.2.1/samples/building.png new file mode 100644 index 0000000..ebabd4f Binary files /dev/null and b/0.2.1/samples/building.png differ diff --git a/0.2.1/samples/citylights.nfo b/0.2.1/samples/citylights.nfo new file mode 100644 index 0000000..0817f11 --- /dev/null +++ b/0.2.1/samples/citylights.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/boat-lake-mountains-snow-italy-6686952/ diff --git a/0.2.1/samples/citylights.png b/0.2.1/samples/citylights.png new file mode 100644 index 0000000..cce56f6 Binary files /dev/null and b/0.2.1/samples/citylights.png differ diff --git a/0.2.1/samples/desk.nfo b/0.2.1/samples/desk.nfo new file mode 100644 index 0000000..98d108e --- /dev/null +++ b/0.2.1/samples/desk.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/coffee-phone-paper-business-792113/ diff --git a/0.2.1/samples/desk.png b/0.2.1/samples/desk.png new file mode 100644 index 0000000..628f46f Binary files /dev/null and b/0.2.1/samples/desk.png differ diff --git a/0.2.1/samples/duckling.nfo b/0.2.1/samples/duckling.nfo new file mode 100644 index 0000000..39c564d --- /dev/null +++ b/0.2.1/samples/duckling.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/chick-ducklings-nature-cute-animal-8160008/ diff --git a/0.2.1/samples/duckling.png b/0.2.1/samples/duckling.png new file mode 100644 index 0000000..14689d0 Binary files /dev/null and b/0.2.1/samples/duckling.png differ diff --git a/0.2.1/samples/girl.nfo b/0.2.1/samples/girl.nfo new file mode 100644 index 0000000..103b0a4 --- /dev/null +++ b/0.2.1/samples/girl.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/waiting-unhappy-woman-rain-china-9588284/ diff --git a/0.2.1/samples/girl.png b/0.2.1/samples/girl.png new file mode 100644 index 0000000..e833b16 Binary files /dev/null and b/0.2.1/samples/girl.png differ diff --git a/0.2.1/samples/meeting.nfo b/0.2.1/samples/meeting.nfo new file mode 100644 index 0000000..375f899 --- /dev/null +++ b/0.2.1/samples/meeting.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/meeting-brainstorming-business-594091/ diff --git a/0.2.1/samples/meeting.png b/0.2.1/samples/meeting.png new file mode 100644 index 0000000..6ec619a Binary files /dev/null and b/0.2.1/samples/meeting.png differ diff --git a/0.2.1/samples/mountains.nfo b/0.2.1/samples/mountains.nfo new file mode 100644 index 0000000..98d108e --- /dev/null +++ b/0.2.1/samples/mountains.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/coffee-phone-paper-business-792113/ diff --git a/0.2.1/samples/mountains.png b/0.2.1/samples/mountains.png new file mode 100644 index 0000000..3d4092e Binary files /dev/null and b/0.2.1/samples/mountains.png differ diff --git a/0.2.1/samples/plane.nfo b/0.2.1/samples/plane.nfo new file mode 100644 index 0000000..ec0c194 --- /dev/null +++ b/0.2.1/samples/plane.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/aircraft-pilatus-pc-24-corporate-jet-5611528/ diff --git a/0.2.1/samples/plane.png b/0.2.1/samples/plane.png new file mode 100644 index 0000000..5e9c557 Binary files /dev/null and b/0.2.1/samples/plane.png differ diff --git a/0.2.1/samples/street.nfo b/0.2.1/samples/street.nfo new file mode 100644 index 0000000..0c5605e --- /dev/null +++ b/0.2.1/samples/street.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/street-japan-city-urban-road-5130030/ diff --git a/0.2.1/samples/street.png b/0.2.1/samples/street.png new file mode 100644 index 0000000..1bdf05f Binary files /dev/null and b/0.2.1/samples/street.png differ diff --git a/0.2.1/zs-convert-to-webp.php b/0.2.1/zs-convert-to-webp.php new file mode 100644 index 0000000..4914893 --- /dev/null +++ b/0.2.1/zs-convert-to-webp.php @@ -0,0 +1,298 @@ +

Error: Image conversion to WebP failed. The original file has been kept.

'; + }); + } + } + + 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']); + ?> + +
+

ZS WebP Converter Settings

+

Version:

+ +

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.

+ +
+ + + + + +
+ + + + +

0 = lowest quality, 101 = lossless

+
+ +
+ + +
+
+
+ +

Test

+
+ + + +
+ + + + + + + + + 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, + ]); +} + diff --git a/0.2/css/webp-test.css b/0.2/css/webp-test.css new file mode 100644 index 0000000..3cdc5eb --- /dev/null +++ b/0.2/css/webp-test.css @@ -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; +} diff --git a/0.2/js/webp-test.js b/0.2/js/webp-test.js new file mode 100644 index 0000000..299e49b --- /dev/null +++ b/0.2/js/webp-test.js @@ -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: ' + response.data.source_info + ''); + sliderContainer.show(); + setOverlayWidth(50); + } else { + alert(response.data || 'Error loading images'); + } + }); + }); + }); +}); diff --git a/0.2/samples/blackboard.nfo b/0.2/samples/blackboard.nfo new file mode 100644 index 0000000..5c5722e --- /dev/null +++ b/0.2/samples/blackboard.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/business-idea-planning-board-3683781/ diff --git a/0.2/samples/blackboard.png b/0.2/samples/blackboard.png new file mode 100644 index 0000000..75fcccc Binary files /dev/null and b/0.2/samples/blackboard.png differ diff --git a/0.2/samples/building.nfo b/0.2/samples/building.nfo new file mode 100644 index 0000000..5751ad0 --- /dev/null +++ b/0.2/samples/building.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/mexico-cdmx-reform-chapultepec-7596566/ diff --git a/0.2/samples/building.png b/0.2/samples/building.png new file mode 100644 index 0000000..ebabd4f Binary files /dev/null and b/0.2/samples/building.png differ diff --git a/0.2/samples/citylights.nfo b/0.2/samples/citylights.nfo new file mode 100644 index 0000000..0817f11 --- /dev/null +++ b/0.2/samples/citylights.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/boat-lake-mountains-snow-italy-6686952/ diff --git a/0.2/samples/citylights.png b/0.2/samples/citylights.png new file mode 100644 index 0000000..cce56f6 Binary files /dev/null and b/0.2/samples/citylights.png differ diff --git a/0.2/samples/desk.nfo b/0.2/samples/desk.nfo new file mode 100644 index 0000000..98d108e --- /dev/null +++ b/0.2/samples/desk.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/coffee-phone-paper-business-792113/ diff --git a/0.2/samples/desk.png b/0.2/samples/desk.png new file mode 100644 index 0000000..628f46f Binary files /dev/null and b/0.2/samples/desk.png differ diff --git a/0.2/samples/duckling.nfo b/0.2/samples/duckling.nfo new file mode 100644 index 0000000..39c564d --- /dev/null +++ b/0.2/samples/duckling.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/chick-ducklings-nature-cute-animal-8160008/ diff --git a/0.2/samples/duckling.png b/0.2/samples/duckling.png new file mode 100644 index 0000000..14689d0 Binary files /dev/null and b/0.2/samples/duckling.png differ diff --git a/0.2/samples/girl.nfo b/0.2/samples/girl.nfo new file mode 100644 index 0000000..103b0a4 --- /dev/null +++ b/0.2/samples/girl.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/waiting-unhappy-woman-rain-china-9588284/ diff --git a/0.2/samples/girl.png b/0.2/samples/girl.png new file mode 100644 index 0000000..e833b16 Binary files /dev/null and b/0.2/samples/girl.png differ diff --git a/0.2/samples/meeting.nfo b/0.2/samples/meeting.nfo new file mode 100644 index 0000000..375f899 --- /dev/null +++ b/0.2/samples/meeting.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/meeting-brainstorming-business-594091/ diff --git a/0.2/samples/meeting.png b/0.2/samples/meeting.png new file mode 100644 index 0000000..6ec619a Binary files /dev/null and b/0.2/samples/meeting.png differ diff --git a/0.2/samples/mountains.nfo b/0.2/samples/mountains.nfo new file mode 100644 index 0000000..98d108e --- /dev/null +++ b/0.2/samples/mountains.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/coffee-phone-paper-business-792113/ diff --git a/0.2/samples/mountains.png b/0.2/samples/mountains.png new file mode 100644 index 0000000..3d4092e Binary files /dev/null and b/0.2/samples/mountains.png differ diff --git a/0.2/samples/plane.nfo b/0.2/samples/plane.nfo new file mode 100644 index 0000000..ec0c194 --- /dev/null +++ b/0.2/samples/plane.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/aircraft-pilatus-pc-24-corporate-jet-5611528/ diff --git a/0.2/samples/plane.png b/0.2/samples/plane.png new file mode 100644 index 0000000..5e9c557 Binary files /dev/null and b/0.2/samples/plane.png differ diff --git a/0.2/samples/street.nfo b/0.2/samples/street.nfo new file mode 100644 index 0000000..0c5605e --- /dev/null +++ b/0.2/samples/street.nfo @@ -0,0 +1 @@ +https://pixabay.com/photos/street-japan-city-urban-road-5130030/ diff --git a/0.2/samples/street.png b/0.2/samples/street.png new file mode 100644 index 0000000..1bdf05f Binary files /dev/null and b/0.2/samples/street.png differ diff --git a/0.2/zs-convert-to-webp.php b/0.2/zs-convert-to-webp.php new file mode 100644 index 0000000..dc9089e --- /dev/null +++ b/0.2/zs-convert-to-webp.php @@ -0,0 +1,298 @@ +

Error: Image conversion to WebP failed. The original file has been kept.

'; + }); + } + } + + 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']); + ?> + +
+

ZS WebP Converter Settings

+

Version:

+ +

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.

+ +
+ + + + + +
+ + + + +

0 = lowest quality, 101 = lossless

+
+ +
+ + +
+
+
+ +

Test

+
+ + + +
+ + + + + + + + + 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, + ]); +} +