Automatically converts images on upload to WebP and AVIF formats using predefined quality settings. Uses imagecreatefromjpeg/png/gif and imagewebp/imageavif functions.
Adds custom fields in media library for per-image quality control of JPEG, WebP, and AVIF.
Bulk conversion utility to convert all existing media library images. Shows progress bar during conversion.
Option to bulk delete all WebP/AVIF images.
Fallback to original image formats if WebP/AVIF is not supported by the browser.
Uses best practices like namespace organization, OOP structure, and exception handling.
class-web-avif-converter.php
class-web-avif-converter.php
attachment_hooks.php
attachment_hooks.php
generator-utils.php
generator-utils.php
utils.php
utils.php
Automatically converts images on upload to WebP and AVIF using predefined quality settings. Uses imagecreatefromjpeg/png/gif and imagewebp/imageavif functions.
Adds custom fields in media library for per-image quality control of JPEG, WebP, and AVIF.
Bulk conversion utility to convert all existing media library images. Shows progress bar during conversion.
Option to bulk delete all WebP/AVIF images.
Fallback to original image formats if WebP/AVIF is not supported by the browser.
Uses best practices like namespace organization, OOP structure, and exception handling.
class-web-avif-converter.php
attachment_hooks.php
generator-utils.php
utils.php
Automatically converts images on upload to WebP and AVIF formats using predefined quality settings. Uses imagecreatefromjpeg/png/gif and imagewebp/imageavif functions.
Adds custom fields in media library for per-image quality control of JPEG, WebP, and AVIF.
Bulk conversion utility to convert all existing media library images. Shows progress bar during conversion.
Option to bulk delete all WebP/AVIF images.
Fallback to original image formats if WebP/AVIF is not supported by the browser.
Uses best practices like namespace organization, OOP structure, and exception handling.
class-web-avif-converter.php
attachment_hooks.php
generator-utils.php
utils.php
class-web-avif-converter.php
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
attachment_hooks.php
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
generator-utils.php
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
utils.php
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
class-web-avif-converter.php
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
attachment_hooks.php
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
generator-utils.php
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
utils.php
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
- 29.6k
- 16
- 45
- 203
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
```
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}
```
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://rekurencja.com/
* @since 1.0.0
* @package Web_Avif_Converter
*
* @wordpress-plugin
* Plugin Name: WebP & Avif Converter
* Plugin URI: https://images.rekurencja.com
* Description: Fast and simple plugin to automatically convert and serve WebP & AVIF images.
* Version: 1.0.0
* Author: Rekurencja
* Author URI: https://rekurencja.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: web-avif-converter
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
define('PHP_REQUIRED_VERSION', '8.1.0');
define('PHP_VERSION_OK', version_compare(phpversion(), PHP_REQUIRED_VERSION, '>='));
define('DEFAULT_QUALITY', 82);
require plugin_dir_path(__FILE__) . 'public/src/utils/utils.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/activation_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/hooks/attachment_hooks.php';
require plugin_dir_path(__FILE__) . 'public/src/utils/generator-utils.php';
// exceptions
require plugin_dir_path(__FILE__) . 'public/src/exceptions/exceptions.php';
// Include admin.php for admin page functionality
if (is_admin()) {
require plugin_dir_path(__FILE__) . 'public/src/admin.php';
}
register_activation_hook(__FILE__, 'WebAvifConverter\Hooks\activate_web_avif_converter');
register_deactivation_hook(__FILE__, 'WebAvifConverter\Hooks\deactivate_web_avif_converter');
add_filter('attachment_fields_to_edit', 'add_image_quality_field', 10, 2);
add_filter('manage_media_columns', 'add_image_quality_column');
add_action('manage_media_custom_column', 'display_image_quality_column', 10, 2);
add_filter('jpeg_quality', 'get_jpeg_quality');
function get_jpeg_quality($quality)
{
return get_option('wac_quality_jpeg', 82);
}
use WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
function add_image_quality_column($columns)
{
$columns['quality_webp'] = 'WebP Quality';
$columns['quality_avif'] = 'Avif Quality';
$columns['quality_jpeg'] = 'Jpeg Quality';
return $columns;
}
function add_image_quality_field($form_fields, $post)
{
// wp_die( print_r( $form_fields ));
$form_fields['quality_webp'] = generate_quality_field($post->ID, 'quality_webp', 'Quality of WEBP');
$form_fields['quality_avif'] = generate_quality_field($post->ID, 'quality_avif', 'Quality of AVIF');
$form_fields['quality_jpeg'] = generate_quality_field($post->ID, 'quality_jpeg', 'Quality of JPEG');
return $form_fields;
}
function determine_image_extension($image_url)
{
$extension = pathinfo($image_url, PATHINFO_EXTENSION);
return strtolower($extension);
}
function generate_quality_field($post_id, $field_name, $field_label)
{
// get post metadata
$metadata = wp_get_attachment_metadata($post_id);
// echo $post_id;
// wp_die(print_r($metadata));
if (isset(wp_get_attachment_metadata($post_id)[$field_name])) {
$field_value = wp_get_attachment_metadata($post_id)[$field_name];
$placeholder_value = $field_value;
} else {
$field_value = '';
$placeholder_value = 'Quality is not set';
}
$input_html = "<input type='number' min='0' max='100' step='1' ";
$input_html .= "name='attachments[$post_id][$field_name]' ";
$input_html .= "value='" . esc_attr($field_value ? $field_value : '') . "' ";
$input_html .= "placeholder='" . esc_attr($placeholder_value) . "'/>";
$field = array(
'label' => $field_label,
'input' => 'html',
'helps' => 'Enter a value between 1 and 100 for image quality (100 = best quality, 1 = worst quality)',
'html' => $input_html,
);
return $field;
}
function display_image_quality_column($column_name, $attachment_id)
{
$allowed_columns = ['quality_avif', 'quality_webp', 'quality_jpeg'];
if (in_array($column_name, $allowed_columns)) {
$attachment_metadata = wp_get_attachment_metadata($attachment_id);
if (isset($attachment_metadata[$column_name])) {
echo $attachment_metadata[$column_name];
} else {
echo 'N/A';
}
}
}
add_action('admin_menu', 'webp_avif_bulk_convert_menu');
function webp_avif_bulk_convert_menu()
{
add_submenu_page(
'options-general.php',
'WebP & Avif Converter',
'WebP & Avif Converter',
'manage_options',
'webp_avif_bulk_convert',
'webp_avif_bulk_convert_page'
);
}
function get_images_size_info()
{
// loop through all images
$images = get_posts(
array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
)
);
$images_size_info = [
"total_original_size" => 0,
"total_avif_size" => 0,
"total_webp_size" => 0,
];
echo "<div class='summary-wrapper'>";
foreach ($images as $image) {
try {
// Determine the extension
$extension = determine_image_extension($image->guid);
// Handle unsupported extensions
if ($extension === 'svg' || $extension === 'gif') {
throw new FilesizeUnavailableException($extension, 'Unsupported extension: ' . $extension);
}
$metadata = wp_get_attachment_metadata($image->ID);
$cur_image_size_info = [
"original_size" => isset($metadata['filesize']) ? $metadata['filesize'] : 0,
"avif_size" => 0,
"webp_size" => 0,
];
if (isset($metadata['sizes'])) {
foreach ($metadata['sizes'] as $size_name => $size) {
$cur_image_size_info["original_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_avif'])) {
foreach ($metadata['sizes_avif'] as $size_name => $size) {
$cur_image_size_info["avif_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (isset($metadata['sizes_webp'])) {
foreach ($metadata['sizes_webp'] as $size_name => $size) {
$cur_image_size_info["webp_size"] += isset($size['filesize']) ? $size['filesize'] : 0;
}
}
if (!isset($metadata['sizes'])) {
throw new FilesizeUnavailableException('Unknown', 'Filesize information not available for this file.');
}
$images_size_info['total_original_size'] += $cur_image_size_info['original_size'];
$images_size_info['total_avif_size'] += $cur_image_size_info['avif_size'];
$images_size_info['total_webp_size'] += $cur_image_size_info['webp_size'];
echo "<div class='summary-item'>";
echo "<img src='" . $image->guid . "' alt='Image ID: " . $image->ID . "' width='150px'>";
echo "<h4>Image ID: " . $image->ID . "</h4>";
echo "<p>Image Title: <b>" . $image->post_title . "</b></p>";
echo "<p>Original size: " . size_format($cur_image_size_info['original_size']) . " </p>";
echo "<p>Avif size: " . size_format($cur_image_size_info['avif_size']) . "</p>";
echo "<p>Webp size: " . size_format($cur_image_size_info['webp_size']) . "</p>";
echo "</div>";
} catch (FilesizeUnavailableException $e) {
echo "Filesize information not available for image ID " . $image->ID . ". " . $e->getMessage();
} catch (Exception $e) {
echo "An error occurred: " . $e->getMessage();
}
}
echo "</div>";
echo "<div class='total-summary-item'>";
echo "<h4>Total Summary:</h4>";
echo "<p>Total Original Image Size: " . size_format($images_size_info['total_original_size']) . "</p>";
echo "<p>Total AVIF Image Size: " . size_format($images_size_info['total_avif_size']) . "</p>";
echo "<p>Total WebP Image Size: " . size_format($images_size_info['total_webp_size']) . "</p>";
echo "</div>";
return $images_size_info;
}
function webp_avif_bulk_convert_page()
{
if (isset($_POST['submit'])) {
$quality_webp = isset($_POST['quality_webp']) ? intval($_POST['quality_webp']) : DEFAULT_QUALITY;
$quality_avif = isset($_POST['quality_avif']) ? intval($_POST['quality_avif']) : DEFAULT_QUALITY;
$quality_jpeg = isset($_POST['quality_jpeg']) ? intval($_POST['quality_jpeg']) : DEFAULT_QUALITY;
if ($_POST['submit'] === 'Set') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
update_option('wac_quality_webp', $quality_webp);
update_option('wac_quality_avif', $quality_avif);
update_option('wac_quality_jpeg', $quality_jpeg);
if (isset($_POST['regenerate']) && $_POST['regenerate'] === 'on') {
echo '<span class="closebtn icon" onclick="this.parentElement.style.display=\'none\';"></span>';
echo '<div class="progress-bar" id="progress-bar" style="width: 0%">0%;
</div>';
Hooks\update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg);
echo '<p class="notification notification-good">
All images have been converted to WebP and Avif formats.
</p>';
}
} else if ($_POST['submit'] === 'Delete') {
echo '<div class="success alert conversion-wrapper" id="alert" [class.visible]="isVisible">';
echo '<div class="content">';
Hooks\delete_all_attachments_avif_and_webp();
echo '<p class="notification notification-bad">
All WebP and Avif images have been deleted.
</p>';
}
echo '</div>';
echo '</div>';
}
?>
<div class="image-conversion-wrapper">
<h1 class="conversion-heading">WebP & Avif Converter </h1>
<form method="post">
<?php $isPhpVersionOk = true; ?> <!-- Define PHP_VERSION_OK -->
<fieldset <?php echo $isPhpVersionOk ? '' : 'disabled' ?>>
<p class="conversion-description">Tool for WebP and Avif format conversion of all images in the
uploads/media library directory. <br>
<b>Choose quality</b> - choose the quality of the converted images (the lower the quality, the smaller
the size) <br>
<b>Delete function</b> - deletes all WebP and Avif images from the uploads/media library directory.
<br>
<b>Convert function</b> - converts all images in the uploads/media library directory to WebP and Avif
formats. <br>
<b>Note: </b> This tool will not convert images that are not in the uploads/media library directory.<br>
<?php echo Utils\get_php_version_info(); ?>
</p>
<div class="conversion-options">
<label class="option-label">Quality of WEBP: (0 - 100%)</label>
<input type="number" name="quality_webp" value="<?php echo get_option('wac_quality_webp', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of AVIF: (0 - 100%)</label>
<input type="number" name="quality_avif" value="<?php echo get_option('wac_quality_avif', DEFAULT_QUALITY); ?>" min="0" max="100">
<label class="option-label">Quality of JPEG: (0 - 100%)</label>
<input type="number" name="quality_jpeg" value="<?php echo get_option('wac_quality_jpeg', DEFAULT_QUALITY); ?>" min="0" max="100">
<input type="checkbox" name="regenerate" id="regenerate" checked>
<label for="regenerate" class="option-label">
Update qualities of existing images
</label>
<div class="toggle-summary_w">
<button id="toggle-summary">Show / Hide Podsumowanie</button>
</div>
<div class="conversion-summary-toggle">
<?php
// You need to define the get_images_size_info function or include the file that contains it.
get_images_size_info();
?>
</div>
</div>
<div class="conversion-buttons">
<input type="submit" name="submit" value="Set" class="convert-button">
<input type="submit" name="submit" value="Delete" class="delete-button">
</div>
</fieldset>
</form>
</div>
<?php
}
/**
* Runs the main functionality of the plugin.
*/
function run_web_avif_converter()
{
// The 'Web_Avif_Converter' class code is included in the 'includes/class-web-avif-converter.php' file.
require plugin_dir_path(__FILE__) . 'includes/class-web-avif-converter.php';
// echo print_r
$plugin = new Web_Avif_Converter();
$plugin->run();
}
run_web_avif_converter();
<?php
namespace WebAvifConverter\Hooks;
use WebAvifConverter\Utils;
use WebAvifConverter\GeneratorUtils;
// function p($a, $die = 0){
// echo '<pre>';
// echo print_r($a);
// echo '</pre>';
// if ($die) { wp_die(); }
// }
apply_filters('jepg_quality', get_option('wac_quality_jpeg', DEFAULT_QUALITY));
add_filter('wp_generate_attachment_metadata', 'WebAvifConverter\Hooks\convert_images_on_generate_attachment_metadata', 10, 2);
function convert_images_on_generate_attachment_metadata($metadata, $attachment_id)
{
if (!isset($metadata['file'])) { return $metadata; }
$extension = pathinfo($metadata['file'], PATHINFO_EXTENSION);
if (!in_array($extension, ['jpg', 'jpeg', 'png'])) { return $metadata; }
$metadata['quality_jpeg'] = get_option('wac_quality_jpeg', DEFAULT_QUALITY);
wp_update_attachment_metadata($attachment_id, $metadata);
// Generating JPEGs with adjusted quality is handled by jepg_quality filter
GeneratorUtils\generate_webp_sizes($attachment_id, get_option('wac_quality_webp', DEFAULT_QUALITY));
GeneratorUtils\generate_avif_sizes($attachment_id, get_option('wac_quality_avif', DEFAULT_QUALITY));
return wp_get_attachment_metadata($attachment_id);
}
add_action('edit_attachment', 'WebAvifConverter\Hooks\save_image_quality_metadata');
function save_image_quality_metadata($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
$filed_names = ['quality_jpeg', 'quality_avif', 'quality_webp' ];
foreach ($filed_names as $filed_name) {
if (empty($_REQUEST['attachments'][$attachment_id][$filed_name])) { continue; }
$quality = intval($_REQUEST['attachments'][$attachment_id][$filed_name]);
if ($quality < 1 || $quality > 100 || isset($metadata[$filed_name]) && $quality === $metadata[$filed_name]) { continue; }
if ($filed_name === 'quality_jpeg') { GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_webp') { GeneratorUtils\generate_webp_sizes($attachment_id, $quality); }
if ($filed_name === 'quality_avif') { GeneratorUtils\generate_avif_sizes($attachment_id, $quality); }
}
}
function update_attachment_quality($attachment_id, $quality_webp = DEFAULT_QUALITY, $quality_avif = DEFAULT_QUALITY, $quality_jpeg = DEFAULT_QUALITY)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata) || !isset($metadata['file']) || !isset($metadata['sizes'])) {
return; // No metadata found for this attachment.
}
if(!isset($metadata['quality_jpeg']) || intval($metadata['quality_jpeg']) !== $quality_jpeg){
GeneratorUtils\generate_jpeg_sizes($attachment_id, $quality_jpeg);
}
if(!isset($metadata['quality_webp']) || intval($metadata['quality_webp']) !== $quality_webp){
GeneratorUtils\generate_webp_sizes($attachment_id, $quality_webp);
}
if(!isset($metadata['quality_avif']) || intval($metadata['quality_avif']) !== $quality_avif){
GeneratorUtils\generate_avif_sizes($attachment_id, $quality_avif);
}
}
function update_all_attachments_quality($quality_webp, $quality_avif, $quality_jpeg)
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
$progress++;
$percent = intval($progress / $total_attachments * 100);
Utils\update_progress_bar($percent);
update_attachment_quality($attachment->ID, $quality_webp, $quality_avif, $quality_jpeg);
}
}
add_filter('delete_attachment', 'WebAvifConverter\Hooks\delete_attachment_files');
function delete_attachment_files($attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$upload_dir = wp_upload_dir()['basedir'];
if (array_key_exists('file_webp', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_webp']);
unset($metadata['file_webp']);
}
if (array_key_exists('file_avif', $metadata)) {
Utils\safe_unlink($upload_dir . '/' . $metadata['file_avif']);
unset($metadata['file_avif']);
}
$attachment_subdir = dirname($metadata['file']);
$attachment_files_dir = $upload_dir . '/' . $attachment_subdir . '/';
if (array_key_exists('sizes_avif', $metadata)) {
foreach ($metadata['sizes_avif'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_avif']);
}
if (array_key_exists('quality_webp', $metadata)){
unset($metadata['quality_webp']);
}
if (array_key_exists('quality_avif', $metadata)){
unset($metadata['quality_avif']);
}
if (array_key_exists('sizes_webp', $metadata)) {
foreach ($metadata['sizes_webp'] as $size) {
Utils\safe_unlink($attachment_files_dir . $size['file']);
}
unset($metadata['sizes_webp']);
}wp_update_attachment_metadata($attachment_id, $metadata);
}
function delete_all_attachments_avif_and_webp()
{
Utils\update_progress_bar(0);
$attachments = get_posts(array(
'post_type' => 'attachment',
'numberposts' => -1,
'post_mime_type' => 'image',
'exclude' => get_post_thumbnail_id()
));
$total_attachments = count($attachments);
$progress = 0;
foreach ($attachments as $attachment) {
delete_attachment_files($attachment->ID);
$progress++;
$percent = intval($progress / $total_attachments * 100) . "%";
Utils\update_progress_bar($percent);
}
}
<?php
namespace WebAvifConverter\GeneratorUtils;
/**
* Convert image to WebP format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted WebP image or void if unable to create an image resource.
*/
function p($a, $die = 0){
echo '<pre>';
echo print_r($a);
echo '</pre>';
if ($die) { wp_die(); }
}
function generate_jpeg(string $path, int $quality = 80){
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagejpeg($image, $output_path . '/' . $filename , $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename;
}
function generate_webp(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imagewebp($image, $output_path . '/' . $filename . '.webp', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.webp';
}
/**
* Convert image to AVIF format
*
* @param string $path Path to the image file.
* @param int $quality Image quality (0 to 100, default is 80).
*
* @return string|void Returns the relative path to the converted AVIF image or void if unable to create an image resource.
*/
function generate_avif(string $path, int $quality = 80)
{
$output_path = dirname($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$filename = basename($path, '.' . $extension);
$image = null;
if ($extension === 'jpeg' || $extension === 'jpg') {
$image = imagecreatefromjpeg($path);
} elseif ($extension === 'png') {
$image = imagecreatefrompng($path);
} elseif ($extension === 'gif') {
$image = imagecreatefromgif($path);
} else {
return; // Unsupported format.
}
if (!$image) {
return; // Unable to create image resource, possibly unsupported format.
}
imageavif($image, $output_path . '/' . $filename . '.avif', $quality);
imagedestroy($image);
$directories = explode('/', dirname($path));
$upload_subdir = implode('/', array_slice($directories, -2));
return $upload_subdir . '/' . $filename . '.avif';
}
function generate_jpeg_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_jpeg'] = $quality;
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
// p($metadata['sizes'], 1);
foreach($metadata['sizes'] as $size_name => $size){
$subsize_path = $image_subdir_path . '/' . $size['file'];
$image = wp_get_image_editor($subsize_path);
$image->set_quality( $quality );
$saved = $image->save($subsize_path);
$metadata['sizes'][$size_name]['filesize'] = filesize($subsize_path);
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_webp_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_webp'] = $quality;
$metadata['sizes_webp'] = [];
$metadata['file_webp'] = generate_webp(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['webp_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_webp']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_webp($original_subsize_path, $quality);
$metadata['sizes_webp'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/webp',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
function generate_avif_sizes($attachment_id, $quality){
$metadata = wp_get_attachment_metadata($attachment_id);
if (empty($metadata)) { return; }
$image_subdir_path = wp_get_upload_dir()['basedir'] . '/' . dirname($metadata['file']);
$metadata['quality_avif'] = $quality;
$metadata['sizes_avif'] = [];
$metadata['file_avif'] = generate_avif(
wp_get_upload_dir()['basedir'] . '/' . $metadata['file'],
$quality
);
$metadata['avif_filesize'] = filesize(wp_get_upload_dir()['basedir'] . '/'. $metadata['file_avif']);
$original_image_path = wp_get_upload_dir()['basedir'] . '/' . $metadata['file'];
// skip svg files path info
if (pathinfo($original_image_path, PATHINFO_EXTENSION) === 'svg') {
return;
}
foreach($metadata['sizes'] as $size_name => $size){
$original_subsize_path = $image_subdir_path . '/' . $size['file'];
//create or overwrite the subsize image
$subsize_subpath = generate_avif($original_subsize_path, $quality);
$metadata['sizes_avif'][$size_name] = [
'file' => basename($subsize_subpath),
'width' => $size['width'],
'height' => $size['height'],
'mime-type' => 'image/avif',
'filesize' => filesize(wp_get_upload_dir()['basedir'] . '/' . $subsize_subpath)
];
}
wp_update_attachment_metadata($attachment_id, $metadata);
};
<?php
namespace WebAvifConverter\Utils;
/**
* Helper function for deleting files.
*/
function safe_unlink($path)
{
if (file_exists($path)) {
unlink($path);
}
}
function update_progress_bar($percent) {
$percent = strval($percent);
echo "<script>
var progressBar = document.getElementById('progress-bar');
progressBar.style.width = '$percent%';
progressBar.innerHTML = '$percent%';
</script>";
ob_flush();
flush();
}
function get_php_version_info()
{
if(version_compare(phpversion(), PHP_REQUIRED_VERSION, '>=')){
return 'PHP Version is: <span class="php-version-good">' . phpversion() . '</span><b> version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
} else {
return 'PHP Version: <span class="php-version-bad">' . phpversion() . '</span> is too low <.>version >= ' . PHP_REQUIRED_VERSION . '</b> is required.';
}
}