handleInputChange( 'webpQuality', value || '' ) }
- min="1"
- max="100"
- step="1"
- __next40pxDefaultSize
- allowReset
- initialPosition={ 80 }
- help={ __( 'Set the quality / compression level for generated .webp images. Default is 80%. Higher values mean better quality and larger file size; lower values reduce file size with more compression but lower quality.', 'cimo-image-optimizer' ) }
+ { /* Smart Optimization */ }
+
+
+ { __( 'Smart Optimization', 'cimo-image-optimizer' ) }
+ { buildType === 'free' && (
+
+ { __( 'Premium', 'cimo-image-optimizer' ) }
+
+ ) }
+
+ }
+ checked={ buildType === 'free' ? false : ( settings.smartOptimization === 1 ) }
+ disabled={ buildType === 'free' }
+ onChange={ checked => handleInputChange( 'smartOptimization', checked ? 1 : 0 ) }
+ help={ __( 'Smart Optimization uses our advanced algorithms to choose the best compression and quality settings per image. This adds a small overhead to the upload process, but the results are even smaller file sizes and faster loading times.', 'cimo-image-optimizer' ) }
/>
+
+ { /* WebP Image Quality */ }
+ { ( buildType === 'free' || settings.smartOptimization === 0 ) && (
+
+ handleInputChange( 'webpQuality', value || '' ) }
+ min="1"
+ max="100"
+ step="1"
+ __next40pxDefaultSize
+ allowReset
+ initialPosition={ 80 }
+ help={ __( 'Set the quality / compression level for generated .webp images. Default is 80%. Higher values mean better quality and larger file size; lower values reduce file size with more compression but lower quality.', 'cimo-image-optimizer' ) }
+ />
+
+ ) }
+
{ /* Maximum Image Dimension */ }
} Promise resolving with the converted file and optional metadata.
+ */
+ async optimize() {
+ return await this.convert()
+ }
+
/**
* Cancel the current conversion.
* Subclasses should override this to implement actual cancellation logic.
diff --git a/src/shared/converters/image-converter.js b/src/shared/converters/image-converter.js
index 13a838f..5ae83ce 100644
--- a/src/shared/converters/image-converter.js
+++ b/src/shared/converters/image-converter.js
@@ -1,5 +1,6 @@
import { Converter } from './converter-abstract'
import { applyFilters, applyFiltersAsync } from '@wordpress/hooks'
+import { __ } from '@wordpress/i18n'
// Supported output formats
const supportedFormats = [
@@ -215,15 +216,15 @@ class ImageConverter extends Converter {
/**
* Convert an image file to the desired format and options.
- * @param {boolean} [force=false] - Force conversion even if the file is already in the desired format.
- * @param {Object} [options] - Options for the conversion.
+ * @param {Object} [options] - Options for the conversion.
* @return {Promise<{file: File|Blob, metadata?: Object}>} Promise resolving to the converted file and optional metadata.
*/
- async convert( force = false, options = {} ) {
+ async convert( options = {} ) {
const file = this.file
const {
quality = this.options?.quality || 0.8,
forceSize = this.options?.forceSize || false,
+ isSmartOptimization = this.options?.isSmartOptimization || false,
} = options
let formatTo = options.format || this.options?.format || ''
@@ -239,15 +240,7 @@ class ImageConverter extends Converter {
}
}
- // Skip if already the desired format
const formatInfo = supportedFormats.find( f => f.value === formatTo )
- if ( ! force && formatInfo && file.type === formatInfo.mimeType ) {
- return {
- file,
- metadata: null,
- reason: 'same-format',
- }
- }
// Check if the browser supports the desired output format
const testCanvas = document.createElement( 'canvas' )
@@ -294,6 +287,23 @@ class ImageConverter extends Converter {
const fileItem = { file }
try {
+ let progressInterval = null
+ // Let the smart optimization process handle the progress updates.
+ if ( ! isSmartOptimization ) {
+ // Initialize the progress to 0.5
+ this.progress = 0.5
+ this._status = __( 'Optimizing…', 'cimo-image-optimizer' )
+ progressInterval = setInterval( () => {
+ const increment = ( Math.random() * 0.05 ) + 0.05 // 0.05 – 0.1
+ this.progress += increment
+
+ if ( this.progress >= 0.9 ) {
+ this.progress = 0.9
+ clearInterval( progressInterval )
+ }
+ }, 500 )
+ }
+
const start = performance.now()
let convertedBlob
@@ -347,6 +357,15 @@ class ImageConverter extends Converter {
lastModified: Date.now(),
} )
+ if ( ! isSmartOptimization ) {
+ if ( progressInterval ) {
+ clearInterval( progressInterval )
+ }
+ // Ensure progress is set to 1 at the end of the process.
+ this._status = __( 'Completed', 'cimo-image-optimizer' )
+ this.progress = 1
+ }
+
return { file: outFile, metadata: conversionMetadata }
} catch ( error ) {
return {
@@ -359,7 +378,29 @@ class ImageConverter extends Converter {
}
async optimize() {
- return await applyFiltersAsync( 'cimo.imageConverter.optimize', this )
+ const isSmartOptimization = this.options?.isSmartOptimization || false
+ let result = null
+
+ if ( isSmartOptimization ) {
+ result = await applyFiltersAsync( 'cimo.imageConverter.optimize', {
+ file: this.file,
+ metadata: null,
+ reason: 'no-optimizer',
+ }, this )
+ }
+
+ if ( ! result || result.reason === 'no-optimizer' ) {
+ result = await this.convert()
+ } else if ( result.metadata && typeof result.metadata === 'object' ) {
+ result = {
+ ...result,
+ metadata: {
+ ...result.metadata,
+ smartOptimized: 1,
+ },
+ }
+ }
+ return result
}
}
diff --git a/src/shared/converters/index.js b/src/shared/converters/index.js
index 7a58c50..86c016e 100644
--- a/src/shared/converters/index.js
+++ b/src/shared/converters/index.js
@@ -38,15 +38,23 @@ export const getFileConverter = _file => {
}
if ( file.type.startsWith( 'image/' ) ) {
- // If the browser doesn't support webp, then we can't convert it.
- if ( ! isFormatSupported( 'webp' ) ) {
- return new NullConverter( file )
+ let format = 'webp'
+
+ // If webp is not supported, use the same format of the file.
+ if ( ! isFormatSupported( format ) ) {
+ format = file.type
}
+
if ( ImageConverter.supportsMimeType( file.type ) ) {
return new ImageConverter( file, {
- format: 'webp',
+ format,
quality: window.cimoSettings?.webpQuality || 0.8,
maxDimension: window.cimoSettings?.maxImageDimension || 0,
+ isSmartOptimization: String( window.cimoSettings?.smartOptimization ?? '0' ) !== '0',
+ // Show progress for image conversion for both smart and non-smart optimization,
+ // as long as the conversion takes more than 700ms.
+ showProgress: true,
+ progressDelay: 700,
} )
}
}