Malin 37cf714058 WebP Express CloudHost.es Fix v0.25.9-cloudhost
 Fixed bulk conversion getting stuck on missing files
 Added robust error handling and timeout protection
 Improved JavaScript response parsing
 Added file existence validation
 Fixed missing PHP class imports
 Added comprehensive try-catch error recovery

🔧 Key fixes:
- File existence checks before conversion attempts
- 30-second timeout protection per file
- Graceful handling of 500 errors and JSON parsing issues
- Automatic continuation to next file on failures
- Cache busting for JavaScript updates

🎯 Result: Bulk conversion now completes successfully even with missing files

🚀 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-23 10:22:32 +02:00

273 lines
9.8 KiB
PHP

<?php
namespace WebPExpress;
class CLI extends \WP_CLI_Command
{
private static function printableSize($bytes) {
return ($bytes < 10000) ? $bytes . " bytes" : round($bytes / 1024) . ' kb';
}
/**
* Convert images to webp
*
* ## OPTIONS
* [<location>]
* : Limit which folders to process to a single location. Ie "uploads/2021". The first part is the
* "image root", which must be "uploads", "themes", "plugins", "wp-content" or "index"
*
* [--reconvert]
* : Even convert images that are already converted (new conversions replaces the old conversions)
*
* [--only-png]
* : Only convert PNG images
*
* [--only-jpeg]
* : Only convert jpeg images
*
* [--quality]
* : Override quality with specified (0-100)
*
* [--near-lossless]
* : Override near-lossless quality with specified (0-100)
*
* [--alpha-quality]
* : Override alpha-quality quality with specified (0-100)
*
* [--encoding]
* : Override encoding quality with specified ("auto", "lossy" or "lossless")
*
* [--converter=<converter>]
* : Specify the converter to use (default is to use the stack). Valid options: cwebp | vips | ewww | imagemagick | imagick | gmagick | graphicsmagick | ffmpeg | gd | wpc | ewww
*/
public function convert($args, $assoc_args)
{
$config = Config::loadConfigAndFix();
$override = [];
if (isset($assoc_args['quality'])) {
$override['max-quality'] = intval($assoc_args['quality']);
$override['png-quality'] = intval($assoc_args['quality']);
}
if (isset($assoc_args['near-lossless'])) {
$override['png-near-lossless'] = intval($assoc_args['near-lossless']);
$override['jpeg-near-lossless'] = intval($assoc_args['near-lossless']);
}
if (isset($assoc_args['alpha-quality'])) {
$override['alpha-quality'] = intval($assoc_args['alpha-quality']);
}
if (isset($assoc_args['encoding'])) {
if (!in_array($assoc_args['encoding'], ['auto', 'lossy', 'lossless'])) {
\WP_CLI::error('encoding must be auto, lossy or lossless');
}
$override['png-encoding'] = $assoc_args['encoding'];
$override['jpeg-encoding'] = $assoc_args['encoding'];
}
if (isset($assoc_args['converter'])) {
if (!in_array($assoc_args['converter'], ConvertersHelper::getDefaultConverterNames())) {
\WP_CLI::error(
'"' . $assoc_args['converter'] . '" is not a valid converter id. ' .
'Valid converters are: ' . implode(', ', ConvertersHelper::getDefaultConverterNames())
);
}
}
$config = array_merge($config, $override);
\WP_CLI::log('Converting with the following settings:');
\WP_CLI::log('- Lossless quality: ' . $config['png-quality'] . ' for PNG, ' . $config['max-quality'] . " for jpeg");
\WP_CLI::log(
'- Near lossless: ' .
($config['png-enable-near-lossless'] ? $config['png-near-lossless'] : 'disabled') . ' for PNG, ' .
($config['jpeg-enable-near-lossless'] ? $config['jpeg-near-lossless'] : 'disabled') . ' for jpeg, '
);
\WP_CLI::log('- Alpha quality: ' . $config['alpha-quality']);
\WP_CLI::log('- Encoding: ' . $config['png-encoding'] . ' for PNG, ' . $config['jpeg-encoding'] . " for jpeg");
if (count($override) == 0) {
\WP_CLI::log('Note that you can override these with --quality=<quality>, etc');
}
\WP_CLI::log('');
$listOptions = BulkConvert::defaultListOptions($config);
if (isset($assoc_args['reconvert'])) {
$listOptions['filter']['only-unconverted'] = false;
}
if (isset($assoc_args['only-png'])) {
$listOptions['filter']['image-types'] = 2;
}
if (isset($assoc_args['only-jpeg'])) {
$listOptions['filter']['image-types'] = 1;
}
if (!isset($args[0])) {
$groups = BulkConvert::getList($config, $listOptions);
foreach($groups as $group){
\WP_CLI::log($group['groupName'] . ' contains ' . count($group['files']) . ' ' .
(isset($assoc_args['reconvert']) ? '' : 'unconverted ') .
'files');
}
\WP_CLI::log('');
} else {
$location = $args[0];
if (strpos($location, '/') === 0) {
$location = substr($location, 1);
}
if (strpos($location, '/') === false) {
$rootId = $location;
$path = '.';
} else {
list($rootId, $path) = explode('/', $location, 2);
}
if (!in_array($rootId, Paths::getImageRootIds())) {
\WP_CLI::error(
'"' . $args[0] . '" is not a valid image root. ' .
'Valid roots are: ' . implode(', ', Paths::getImageRootIds())
);
}
$root = Paths::getAbsDirById($rootId) . '/' . $path;
if (!file_exists($root)) {
\WP_CLI::error(
'"' . $args[0] . '" does not exist. '
);
}
$listOptions['root'] = $root;
$groups = [
[
'groupName' => $args[0],
'root' => $root,
'files' => BulkConvert::getListRecursively('.', $listOptions)
]
];
if (count($groups[0]['files']) == 0) {
\WP_CLI::log('Nothing to convert in ' . $args[0]);
}
}
$orgTotalFilesize = 0;
$webpTotalFilesize = 0;
$converter = null;
$convertOptions = null;
if (isset($assoc_args['converter'])) {
$converter = $assoc_args['converter'];
$convertOptions = Config::generateWodOptionsFromConfigObj($config)['webp-convert']['convert'];
// find the converter
$optionsForThisConverter = null;
foreach ($convertOptions['converters'] as $c) {
if ($c['converter'] == $converter) {
$optionsForThisConverter = (isset($c['options']) ? $c['options'] : []);
break;
}
}
if (!is_array($optionsForThisConverter)) {
\WP_CLI::error('Failed handling options');
}
$convertOptions = array_merge($convertOptions, $optionsForThisConverter);
unset($convertOptions['converters']);
}
foreach($groups as $group){
if (count($group['files']) == 0) continue;
\WP_CLI::log('Converting ' . count($group['files']) . ' files in ' . $group['groupName']);
\WP_CLI::log('------------------------------');
$root = $group['root'];
$files = array_reverse($group['files']);
//echo count($group["files"]);
foreach($files as $key => $file)
{
$path = trailingslashit($group['root']) . $file;
\WP_CLI::log('Converting: ' . $file);
$result = Convert::convertFile($path, $config, $convertOptions, $converter);
if ($result['success']) {
$orgSize = $result['filesize-original'];
$webpSize = $result['filesize-webp'];
$orgTotalFilesize += $orgSize;
$webpTotalFilesize += $webpSize;
//$percentage = round(($orgSize - $webpSize)/$orgSize * 100);
$percentage = ($orgSize == 0 ? 100 : round(($webpSize/$orgSize) * 100));
\WP_CLI::log(
\WP_CLI::colorize(
"%GOK%n. " .
"Size: " .
($percentage<90 ? "%G" : ($percentage<100 ? "%Y" : "%R")) .
$percentage .
"% %nof original" .
" (" . self::printableSize($orgSize) . ' => ' . self::printableSize($webpSize) .
") "
)
);
//print_r($result);
} else {
\WP_CLI::log(
\WP_CLI::colorize("%RConversion failed. " . $result['msg'] . "%n")
);
}
}
}
if ($orgTotalFilesize > 0) {
$percentage = ($orgTotalFilesize == 0 ? 100 : round(($webpTotalFilesize/$orgTotalFilesize) * 100));
\WP_CLI::log(
\WP_CLI::colorize(
"Done. " .
"Size of webps: " .
($percentage<90 ? "%G" : ($percentage<100 ? "%Y" : "%R")) .
$percentage .
"% %nof original" .
" (" . self::printableSize($orgTotalFilesize) . ' => ' . self::printableSize($webpTotalFilesize) .
") "
)
);
}
}
/**
* Flush webps
*
* ## OPTIONS
* [--only-png]
* : Only flush webps that are conversions of a PNG)
*/
public function flushwebp($args, $assoc_args)
{
$config = Config::loadConfigAndFix();
$onlyPng = isset($assoc_args['only-png']);
if ($onlyPng) {
\WP_CLI::log('Flushing webp files that are conversions of PNG images');
} else {
\WP_CLI::log('Flushing all webp files');
}
$result = CachePurge::purge($config, $onlyPng);
\WP_CLI::log(
\WP_CLI::colorize("%GFlushed " . $result['delete-count'] . " webp files%n")
);
if ($result['fail-count'] > 0) {
\WP_CLI::log(
\WP_CLI::colorize("%RFailed deleting " . $result['fail-count'] . " webp files%n")
);
}
}
}