' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -124,7 +124,7 @@ public function can_be_processed() {
return new \WP_Error(
'not_a_file',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'This does not seem to be a file: %s.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -135,7 +135,7 @@ public function can_be_processed() {
return new \WP_Error(
'not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -148,7 +148,7 @@ public function can_be_processed() {
return new \WP_Error(
'folder_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The folder %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $parent_folder ) ) . '
'
)
@@ -197,7 +197,7 @@ public function resize( $dimensions = [], $max_width = 0 ) {
return new \WP_Error(
'not_an_image',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -321,7 +321,7 @@ public function create_thumbnail( $destination ) {
return new \WP_Error(
'not_an_image',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -474,7 +474,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the media file.
* @param array $args Arguments passed to the method.
- */
+ */
do_action( 'imagify_before_optimize_file', $this->path, $args );
/**
@@ -485,7 +485,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the image file.
* @param bool $backup True if a backup will be make.
- */
+ */
do_action_deprecated( 'before_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
if ( $args['backup'] ) {
@@ -509,6 +509,7 @@ public function optimize( $args = [] ) {
if ( $args['convert'] ) {
$data['convert'] = $args['convert'];
+ $format = $args['convert'];
}
$response = upload_imagify_image( [
@@ -534,8 +535,8 @@ public function optimize( $args = [] ) {
$args['convert'] = '';
}
- if ( 'webp' === $args['convert'] ) {
- $destination_path = $this->get_path_to_webp();
+ if ( 'webp' === $args['convert'] || 'avif' === $args['convert'] ) {
+ $destination_path = $this->get_path_to_next_gen( $args['convert'] );
$this->path = $destination_path;
$this->file_type = null;
$this->editor = null;
@@ -557,7 +558,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the image file.
* @param bool $backup True if a backup has been made.
- */
+ */
do_action_deprecated( 'after_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
/**
@@ -568,7 +569,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the media file.
* @param array $args Arguments passed to the method.
- */
+ */
do_action( 'imagify_after_optimize_file', $this->path, $args );
return $response;
@@ -603,7 +604,7 @@ protected function get_editor() {
$this->editor = new \WP_Error(
'image_editor',
sprintf(
- /* translators: %1$s is an error message, %2$s is a "More info?" link. */
+ /* translators: %1$s is an error message, %2$s is a "More info?" link. */
__( 'No php extensions are available to edit images on the server. ImageMagick or GD is required. The internal error is: %1$s. %2$s', 'imagify' ),
$this->editor->get_error_message(),
'' . __( 'More info?', 'imagify' ) . ''
@@ -765,6 +766,26 @@ public function get_path_to_webp() {
return imagify_path_to_webp( $this->path );
}
+ /**
+ * Replace the file extension by its next-gen format extension.
+ *
+ * @since 2.2
+ *
+ * @param string $format the format we are targeting.
+ * @return string|bool The file path on success. False if not an image or on failure.
+ */
+ public function get_path_to_next_gen( string $format ) {
+ if ( ! $this->is_image() ) {
+ return false;
+ }
+
+ if ( $this->is_webp() || $this->is_avif() ) {
+ return false;
+ }
+
+ return imagify_path_to_next_gen( $this->path, $format );
+ }
+
/**
* Tell if the file is a WebP image.
* Rejects "path/to/.webp" files.
@@ -778,6 +799,18 @@ public function is_webp() {
return preg_match( '@(?!^|/|\\\)\.webp$@i', $this->path );
}
+ /**
+ * Tell if the file is an AVIF image.
+ * Rejects "path/to/.avif" files.
+ *
+ * @since 2.2
+ *
+ * @return bool
+ */
+ public function is_avif() {
+ return preg_match( '@(?!^|/|\\\)\.avif$@i', $this->path );
+ }
+
/**
* Get the file mime type + file extension.
*
diff --git a/classes/Optimization/Process/AbstractProcess.php b/classes/Optimization/Process/AbstractProcess.php
index 141355bfb..2d121895c 100644
--- a/classes/Optimization/Process/AbstractProcess.php
+++ b/classes/Optimization/Process/AbstractProcess.php
@@ -95,6 +95,14 @@ abstract class AbstractProcess implements ProcessInterface {
*/
protected $options = [];
+ /**
+ * Tells the format we are currently processing
+ *
+ * @var string
+ * @since 2.2
+ */
+ protected $format;
+
/**
* The constructor.
*
@@ -115,6 +123,7 @@ public function __construct( $id ) {
}
$this->filesystem = \Imagify_Filesystem::get_instance();
+ $this->format = $this->get_current_format();
}
/**
@@ -264,13 +273,13 @@ public function optimize( $optimization_level = null, $args = [] ) {
return new WP_Error( 'optimized', __( 'This media has already been optimized by Imagify.', 'imagify' ) );
}
- if ( $data->is_already_optimized() && $this->has_webp() ) {
+ if ( $data->is_already_optimized() && $this->has_next_gen() ) {
// If already optimized but has WebP, delete WebP versions and optimization data.
$data->delete_optimization_data();
- $deleted = $this->delete_webp_files();
+ $deleted = $this->delete_next_gen_files();
if ( is_wp_error( $deleted ) ) {
- return new WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) );
+ return new WP_Error( 'next_gen_not_deleted', __( 'Previous Next-Gen files could not be deleted.', 'imagify' ) );
}
}
@@ -333,10 +342,13 @@ public function reoptimize( $optimization_level = null, $args = [] ) {
* @see MediaOptimization->task_before()
* @see MediaOptimization->task_after()
*
+ * @since 2.2
+ * Addition of the image format
+ *
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
* @param array $args {
- * An array of optionnal arguments.
+ * An array of optionnal arguments.
*
* @type string $hook_suffix Suffix used to trigger hooks before and after optimization.
* }
@@ -366,40 +378,40 @@ public function optimize_sizes( $sizes, $optimization_level = null, $args = [] )
}
if ( $media->is_image() ) {
- if ( $this->get_option( 'convert_to_webp' ) ) {
- // Add WebP convertion.
+ if ( $this->get_option( 'convert_to_avif' ) ) {
+ // Add Next-Gen convertion.
$files = $media->get_media_files();
foreach ( $sizes as $size_name ) {
if ( empty( $files[ $size_name ] ) ) {
continue;
}
- if ( 'image/webp' === $files[ $size_name ]['mime-type'] ) {
+ if ( $this->get_mime_type( $this->format ) === $files[ $size_name ]['mime-type'] ) {
continue;
}
- if ( in_array( $size_name . static::WEBP_SUFFIX, $sizes, true ) ) {
+ if ( in_array( $size_name . $this->format, $sizes, true ) ) {
continue;
}
- array_unshift( $sizes, $size_name . static::WEBP_SUFFIX );
+ array_unshift( $sizes, $size_name . $this->format );
}
}
if ( ! $media->get_context_instance()->can_backup() && ! $media->get_backup_path() && ! $this->get_data()->get_size_data( 'full', 'success' ) ) {
/**
* Backup is NOT activated, and a backup file does NOT exist yet, and the full size is NOT optimized yet.
- * WebP conversion needs a backup file, even a temporary one: we’ll create one.
+ * Next-Gen conversion needs a backup file, even a temporary one: we’ll create one.
*/
- $webp = false;
+ $next_gen = false;
foreach ( $sizes as $size_name ) {
- if ( $this->is_size_webp( $size_name ) ) {
- $webp = true;
+ if ( $this->is_size_next_gen( $size_name ) ) {
+ $next_gen = true;
break;
}
}
- if ( $webp ) {
+ if ( $next_gen ) {
// We have at least one WebP conversion to do: create a temporary backup.
$backuped = $this->get_original_file()->backup( $media->get_raw_backup_path() );
@@ -463,13 +475,13 @@ public function optimize_size( $size, $optimization_level = null ) {
$media = $this->get_media();
$sizes = $media->get_media_files();
$thumb_size = $size;
- $webp = $this->is_size_webp( $size );
+ $next_gen = $this->is_size_next_gen( $size );
$path_is_temp = false;
- if ( $webp ) {
+ if ( $next_gen ) {
// We'll make sure the file is an image later.
- $thumb_size = $webp; // Contains the name of the non-WebP size.
- $webp = true;
+ $thumb_size = $next_gen; // Contains the name of the non-WebP size.
+ $next_gen = true;
}
if ( empty( $sizes[ $thumb_size ]['path'] ) ) { // Bail out.
@@ -477,7 +489,7 @@ public function optimize_size( $size, $optimization_level = null ) {
return new WP_Error(
'unknown_size',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is unknown.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -486,12 +498,12 @@ public function optimize_size( $size, $optimization_level = null ) {
if ( $this->get_data()->get_size_data( $size, 'success' ) ) { // Bail out.
// This size is already optimized with Imagify, and must not be optimized again.
- if ( $webp ) {
+ if ( $next_gen ) {
return new WP_Error(
'size_is_successfully_optimized',
sprintf(
- /* translators: %s is a size name. */
- __( 'The WebP format for the size %s already exists.', 'imagify' ),
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen format for the size %s already exists.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
);
@@ -499,7 +511,7 @@ public function optimize_size( $size, $optimization_level = null ) {
return new WP_Error(
'size_is_successfully_optimized',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is already optimized by Imagify.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -514,16 +526,16 @@ public function optimize_size( $size, $optimization_level = null ) {
$optimization_level = $this->sanitize_optimization_level( $optimization_level );
- if ( $webp && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) {
+ if ( $next_gen && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) {
// We want a WebP version but the source file is already optimized by Imagify.
$result = $this->create_temporary_copy( $thumb_size, $sizes );
if ( ! $result ) { // Bail out.
// Could not create a copy of the non-WebP version.
$response = new WP_Error(
- 'non_webp_copy_failed',
+ 'non_next_gen_copy_failed',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'Could not create an unoptimized copy of the size %s.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -556,7 +568,7 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'extension_not_supported',
sprintf(
- /* translators: %s is a file extension. */
+ /* translators: %s is a file extension. */
__( '%s cannot be optimized.', 'imagify' ),
'' . esc_html( strtolower( $extension ) ) . '
'
)
@@ -572,14 +584,14 @@ public function optimize_size( $size, $optimization_level = null ) {
return $response;
}
- if ( $webp && ! $file->is_image() ) { // Bail out.
+ if ( $next_gen && ! $file->is_image() ) { // Bail out.
if ( $path_is_temp ) {
$this->filesystem->delete( $path );
}
$response = new WP_Error(
- 'no_webp',
- __( 'This file is not an image and cannot be converted to WebP format.', 'imagify' )
+ 'no_next_gen',
+ __( 'This file is not an image and cannot be converted to Next-Gen format.', 'imagify' )
);
$this->update_size_optimization_data( $response, $size, $optimization_level );
@@ -603,7 +615,7 @@ public function optimize_size( $size, $optimization_level = null ) {
* @param bool $webp The image will be converted to WebP.
* @param bool $is_disabled Tell if this size is disabled from optimization.
*/
- $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled );
+ $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled );
if ( ! is_wp_error( $response ) ) {
if ( $is_disabled ) {
@@ -611,7 +623,7 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'unauthorized_size',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is not authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -620,12 +632,12 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'file_not_exists',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to exist.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '
'
)
);
- } elseif ( $webp && ! $this->can_create_webp_version( $file->get_path() ) ) {
+ } elseif ( $next_gen && ! $this->can_create_next_gen_version( $file->get_path() ) ) {
$response = new WP_Error(
'is_animated_gif',
__( 'This file is an animated gif: since Imagify does not support animated WebP, WebP creation for animated gif is disabled.', 'imagify' )
@@ -634,7 +646,7 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'file_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '
'
)
@@ -650,19 +662,19 @@ public function optimize_size( $size, $optimization_level = null ) {
'backup_path' => $media->get_raw_backup_path(),
'backup_source' => 'full' === $thumb_size ? $media->get_original_path() : null,
'optimization_level' => $optimization_level,
- 'convert' => $webp ? 'webp' : '',
+ 'convert' => $next_gen ? ( static::AVIF_SUFFIX === $this->format ? 'avif' : 'webp' ) : '',
'keep_exif' => true,
'context' => $media->get_context(),
'original_size' => $response['file_size'],
] );
- $response = $this->compare_webp_file_size( [
- 'response' => $response,
- 'file' => $file,
- 'is_webp' => $webp,
- 'non_webp_thumb_size' => $thumb_size,
- 'non_webp_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file.
- 'optimization_level' => $optimization_level,
+ $response = $this->compare_next_gen_file_size( [
+ 'response' => $response,
+ 'file' => $file,
+ 'non_next_gen_thumb_size' => $next_gen,
+ 'non_next_gen_file_path' => $thumb_size,
+ 'non_webp_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file.
+ 'optimization_level' => $optimization_level,
] );
if ( property_exists( $response, 'message' ) ) {
@@ -690,7 +702,7 @@ public function optimize_size( $size, $optimization_level = null ) {
* @param bool $webp The image was supposed to be converted to WebP.
* @param bool $is_disabled Tell if this size is disabled from optimization.
*/
- do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled );
+ do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled );
if ( ! $path_is_temp ) {
return $data;
@@ -711,6 +723,112 @@ public function optimize_size( $size, $optimization_level = null ) {
return $data;
}
+ /**
+ * Compare the file size of a file and its Next-Gen version: if the Next-Gen version is heavier than the non-WebP file, delete it.
+ *
+ * @since 2.2
+ *
+ * @param array $args {
+ * A list of mandatory arguments.
+ *
+ * @type \sdtClass|\WP_Error $response Optimized image data. A \WP_Error object on error.
+ * @type File $file The File instance of the file currently being optimized.
+ * @type bool $is_next_gen Tell if we're requesting a WebP file.
+ * @type string $non_next_gen_thumb_size Name of the corresponding non-next-gen thumbnail size. If we're not creating a Next-Gen file, this corresponds to the current thumbnail size.
+ * @type string $non_next_gen_file_path Path to the corresponding non-next-gen file. If we're not creating a Next-Gen file, this corresponds to the current file path.
+ * @type string $optimization_level The optimization level.
+ * }
+ * @return \sdtClass|WP_Error Optimized image data. A WP_Error object on error.
+ */
+ protected function compare_next_gen_file_size( $args ) {
+ static $keep_large_next_gen;
+
+ if ( ! isset( $keep_large_next_gen ) ) {
+ /**
+ * Allow to not store WebP images that are larger than their non-WebP version.
+ *
+ * @since 1.9.4
+ *
+ * @param bool $keep_large_webp Set to false if you prefer your visitors over your Pagespeed score. Default value is true.
+ */
+ $keep_large_next_gen = apply_filters( 'imagify_keep_large_next_gen', true );
+ }
+
+ if ( $keep_large_next_gen || is_wp_error( $args['response'] ) || ! $args['file']->is_image() ) {
+ return $args['response'];
+ }
+
+ // Optimization succeeded.
+ if ( ! property_exists( $args['response'], 'message' ) && $args['is_next_gen'] ) {
+ /**
+ * We just created a WebP version:
+ * Check if it is lighter than the (maybe optimized) non-WebP file.
+ */
+ $data = $this->get_data()->get_size_data( $args['non_next_gen_thumb_size'] );
+
+ if ( ! $data ) {
+ // We haven’t tried to optimize the non-WebP size yet.
+ return $args['response'];
+ }
+
+ if ( ! empty( $data['optimized_size'] ) ) {
+ // The non-WebP size is optimized, we know the file size.
+ $non_next_gen_file_size = $data['optimized_size'];
+ } else {
+ // The non-WebP size is "already optimized" or "error": grab the file size directly from the file.
+ $non_next_gen_file_size = $this->filesystem->size( $args['non_next_gen_file_path'] );
+ }
+
+ if ( ! $non_next_gen_file_size || $non_next_gen_file_size > $args['response']->new_size ) {
+ // The new WebP file is lighter.
+ return $args['response'];
+ }
+
+ // The new WebP file is heavier than the non-WebP file: delete it and return an error.
+ $this->filesystem->delete( $args['file']->get_path() );
+
+ return new WP_Error(
+ 'next_gen_heavy',
+ sprintf(
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ),
+ '' . esc_html( $args['non_webp_thumb_size'] ) . '
'
+ )
+ );
+ }
+
+ /**
+ * We just created a non-WebP version:
+ * Check if its WebP version file is lighter than this one.
+ */
+ $next_gen_size = $args['non_next_gen_thumb_size'] . $this->format;
+ $next_gen_file_size = $this->get_data()->get_size_data( $next_gen_size, 'optimized_size' );
+
+ if ( property_exists( $args['response'], 'message' ) || ! $next_gen_file_size || $next_gen_file_size < $args['response']->new_size ) {
+ // The WebP file is lighter than this one.
+ return $args['response'];
+ }
+
+ // The new optimized file is lighter than the next-gen file: delete the next-gen file and store an error.
+ $next_gen_path = $args['file']->get_path_to_next_gen( $this->format );
+
+ if ( $next_gen_path && $this->filesystem->is_writable( $next_gen_path ) ) {
+ $this->filesystem->delete( $next_gen_path );
+ }
+
+ $webp_response = new WP_Error(
+ 'next_gen_heavy',
+ sprintf(
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ),
+ '' . esc_html( $args['non_webp_thumb_size'] ) . '
'
+ )
+ );
+
+ $this->update_size_optimization_data( $webp_response, $next_gen_size, $args['optimization_level'] );
+
+ return $args['response'];
+ }
/**
* Compare the file size of a file and its WebP version: if the WebP version is heavier than the non-WebP file, delete it.
*
@@ -778,7 +896,7 @@ protected function compare_webp_file_size( $args ) {
return new WP_Error(
'webp_heavy',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ),
'' . esc_html( $args['non_webp_thumb_size'] ) . '
'
)
@@ -807,7 +925,7 @@ protected function compare_webp_file_size( $args ) {
$webp_response = new WP_Error(
'webp_heavy',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ),
'' . esc_html( $args['non_webp_thumb_size'] ) . '
'
)
@@ -902,7 +1020,7 @@ public function restore() {
$media->update_dimensions();
// Delete the WebP version.
- $this->delete_webp_file( $original_path );
+ $this->delete_next_gen_file( $original_path );
// Restore the thumbnails.
$response = $this->restore_thumbnails();
@@ -943,7 +1061,7 @@ protected function restore_thumbnails() {
* In that case we must also delete the WebP file associated to the full size.
*/
$keep_full_webp = $media->get_raw_original_path() === $media->get_raw_fullsize_path();
- $this->delete_webp_files( $keep_full_webp );
+ $this->delete_next_gen_files( $keep_full_webp );
// Generate new thumbnails.
return $media->generate_thumbnails();
@@ -1197,7 +1315,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'no_dimensions',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Resizing failed: %s', 'imagify' ),
__( 'Imagify could not get the image dimensions.', 'imagify' )
)
@@ -1223,7 +1341,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'resize_failure',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Resizing failed: %s', 'imagify' ),
$resized_path->get_error_message()
)
@@ -1239,7 +1357,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'backup_failure',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Backup failed: %s', 'imagify' ),
$backuped->get_error_message()
)
@@ -1318,75 +1436,36 @@ protected function can_backup( $size ) {
}
+
/** ----------------------------------------------------------------------------------------- */
- /** WEBP ==================================================================================== */
+ /** WEBP & AVIF ============================================================================= */
/** ----------------------------------------------------------------------------------------- */
/**
- * Generate WebP images if they are missing.
- *
- * @since 1.9
+ * Get MIME type based on the image format.
*
- * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
+ * @param string $format Image format ('webp' or 'avif').
+ * @return string|bool The MIME type if valid format, false otherwise.
*/
- public function generate_webp_versions() {
- if ( ! $this->is_valid() ) {
- return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
- }
-
- $media = $this->get_media();
-
- if ( ! $media->is_image() ) {
- return new WP_Error( 'no_webp', __( 'This media is not an image and cannot be converted to WebP format.', 'imagify' ) );
- }
-
- if ( ! $media->has_backup() ) {
- return new WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) );
- }
-
- $data = $this->get_data();
-
- if ( ! $data->is_optimized() && ! $data->is_already_optimized() ) {
- return new WP_Error( 'not_optimized', __( 'This media has not been optimized by Imagify yet.', 'imagify' ) );
- }
-
- if ( $this->has_webp() ) {
- return new WP_Error( 'has_webp', __( 'This media already has WebP versions.', 'imagify' ) );
- }
-
- $files = $media->get_media_files();
- $sizes = [];
- $args = [
- 'hook_suffix' => 'generate_webp_versions',
+ private function get_mime_type( $format ) {
+ $mime_types = [
+ static::AVIF_SUFFIX => 'image/avif',
+ static::WEBP_SUFFIX => 'image/webp',
];
- foreach ( $files as $size_name => $file ) {
- if ( 'image/webp' !== $files[ $size_name ]['mime-type'] ) {
- array_unshift( $sizes, $size_name . static::WEBP_SUFFIX );
- }
- }
-
- if ( ! $sizes ) {
- return new \WP_Error( 'no_sizes', __( 'This media does not have files that can be converted to WebP format.', 'imagify' ) );
- }
-
- $optimization_level = $data->get_optimization_level();
-
- // Optimize.
- return $this->optimize_sizes( $sizes, $optimization_level, $args );
+ return isset( $mime_types[ $format ] ) ? $mime_types[ $format ] : false;
}
/**
- * Delete the WebP images.
+ * Delete the next gen format images.
* This doesn't delete the related optimization data.
*
- * @since 1.9
- * @since 1.9.6 Return WP_Error or true.
+ * @since 2.2
*
* @param bool $keep_full Set to true to keep the full size.
* @return bool|WP_Error True on success. A \WP_Error object on failure.
*/
- public function delete_webp_files( $keep_full = false ) {
+ public function delete_next_gen_files( $keep_full = false ) {
if ( ! $this->is_valid() ) {
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
}
@@ -1411,7 +1490,7 @@ public function delete_webp_files( $keep_full = false ) {
foreach ( $files as $file ) {
if ( 0 === strpos( $file['mime-type'], 'image/' ) ) {
- $deleted = $this->delete_webp_file( $file['path'] );
+ $deleted = $this->delete_next_gen_file( $file['path'] );
if ( is_wp_error( $deleted ) ) {
++$error_count;
@@ -1423,7 +1502,7 @@ public function delete_webp_files( $keep_full = false ) {
return new WP_Error(
'files_not_deleted',
sprintf(
- /* translators: %s is a formatted number, don’t use %d. */
+ /* translators: %s is a formatted number, don’t use %d. */
_n( '%s file could not be deleted.', '%s files could not be deleted.', $error_count, 'imagify' ),
number_format_i18n( $error_count )
)
@@ -1434,62 +1513,61 @@ public function delete_webp_files( $keep_full = false ) {
}
/**
- * Delete a WebP image, given its non-WebP version's path.
+ * Delete a next gen format image, given its non-WebP version's path.
* This doesn't delete the related optimization data.
*
- * @since 1.9
- * @since 1.9.6 Return WP_Error or true.
+ * @since 2.2
*
- * @param string $file_path Path to the non-WebP file.
+ * @param string $file_path Path to the non-next-gen file.
* @return bool|WP_Error True on success. A \WP_Error object on failure.
*/
- protected function delete_webp_file( $file_path ) {
+ protected function delete_next_gen_file( $file_path ) {
if ( ! $file_path ) {
- return new WP_Error( 'no_path', __( 'Path to non-WebP file not provided.', 'imagify' ) );
+ return new WP_Error( 'no_path', __( 'Path to non-next-gen file not provided.', 'imagify' ) );
}
- $webp_file = new File( $file_path );
- $webp_path = $webp_file->get_path_to_webp();
+ $next_gen_file = new File( $file_path );
+ $next_gen_path = $next_gen_file->get_path_to_next_gen( $this->format );
- if ( ! $webp_path ) {
- return new WP_Error( 'no_webp_path', __( 'Could not get the path to the WebP file.', 'imagify' ) );
+ if ( ! $next_gen_path ) {
+ return new WP_Error( 'no_$next_gen_path', __( 'Could not get the path to the Next-Gen format file.', 'imagify' ) );
}
- if ( ! $this->filesystem->exists( $webp_path ) ) {
+ if ( ! $this->filesystem->exists( $next_gen_path ) ) {
return true;
}
- if ( ! $this->filesystem->is_writable( $webp_path ) ) {
+ if ( ! $this->filesystem->is_writable( $next_gen_path ) ) {
return new WP_Error(
'file_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
- if ( ! $this->filesystem->is_file( $webp_path ) ) {
+ if ( ! $this->filesystem->is_file( $next_gen_path ) ) {
return new WP_Error(
'not_a_file',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'This does not seem to be a file: %s.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
- $deleted = $this->filesystem->delete( $webp_path, false, 'f' );
+ $deleted = $this->filesystem->delete( $next_gen_path, false, 'f' );
if ( ! $deleted ) {
return new WP_Error(
'file_not_deleted',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s could not be deleted.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
@@ -1497,19 +1575,30 @@ protected function delete_webp_file( $file_path ) {
return true;
}
+
/**
- * Tell if a thumbnail size is an "Imagify WebP" size.
+ * Gives the next-gen image format we are processing.
+ *
+ * @return string Current format we are targeting.
+ */
+ public function get_current_format() {
+ return $this->get_option( 'convert_to_avif' ) ? static::AVIF_SUFFIX : static::WEBP_SUFFIX;
+ }
+
+ /**
+ * Tell if a thumbnail size is an "Imagify Next-Gen" size.
*
* @since 1.9
+ * @since 2.2 addition of the format parameter.
*
* @param string $size_name The size name.
* @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
*/
- public function is_size_webp( $size_name ) {
+ public function is_size_next_gen( $size_name ) {
static $suffix;
if ( ! isset( $suffix ) ) {
- $suffix = preg_quote( static::WEBP_SUFFIX, '/' );
+ $suffix = preg_quote( $this->format, '/' );
}
if ( preg_match( '/^(?