Skip to content

Convert images to optimized formats #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 124 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
bfd4905
Add format options
tijmenbruggeman Mar 5, 2025
b8f8f3f
Add settings
tijmenbruggeman Mar 14, 2025
c845bf8
Add output examples
tijmenbruggeman Mar 14, 2025
bf23f86
Add mock responses
tijmenbruggeman Mar 14, 2025
e784e21
Add file extension replacement
tijmenbruggeman Mar 14, 2025
d6673f5
Add conversion actions to compression clients
tijmenbruggeman Mar 14, 2025
02fe1b9
Applied conversion
tijmenbruggeman Mar 14, 2025
f6684df
Replace content with picture
tijmenbruggeman Mar 14, 2025
bb2bbc0
Import picture
tijmenbruggeman Mar 14, 2025
ecce404
Add launch json for debugging
tijmenbruggeman Mar 14, 2025
fe5980a
Simplify conversion settings
tijmenbruggeman Mar 19, 2025
2237987
If original is already converted
tijmenbruggeman Mar 19, 2025
d0cf464
Show compression details
tijmenbruggeman Mar 19, 2025
302124d
Add constants
tijmenbruggeman Mar 20, 2025
bdc15fd
Add conversion to fopen client
tijmenbruggeman Mar 20, 2025
83bd883
reformat
tijmenbruggeman Mar 20, 2025
8d7f6b5
Use constant
tijmenbruggeman Mar 20, 2025
af81edf
delete converted images
tijmenbruggeman Mar 20, 2025
4344f47
Add settings test
tijmenbruggeman Mar 20, 2025
dcba608
Replace labels
tijmenbruggeman Mar 20, 2025
0dc4be4
Set correct mocks
tijmenbruggeman Mar 20, 2025
45a7fcd
Format
tijmenbruggeman Mar 31, 2025
57ffc12
Add option to settings
tijmenbruggeman Mar 31, 2025
39f5848
Add conversion test case
tijmenbruggeman Mar 31, 2025
d7ffd8e
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Apr 3, 2025
afa1550
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Apr 3, 2025
32af07f
get mimetype from input file
tijmenbruggeman Apr 7, 2025
f51c7d1
Change register to register_once
tijmenbruggeman Apr 8, 2025
3f839cc
Add test case for convert and not replace
tijmenbruggeman Apr 8, 2025
6d1fbf5
Remove convert attribute
tijmenbruggeman Apr 8, 2025
a2acb31
Set callback after convert
tijmenbruggeman Apr 8, 2025
55836ca
Add stream support
tijmenbruggeman Apr 8, 2025
00d22c4
Add unit cache to ignore
tijmenbruggeman Apr 8, 2025
1131414
Format
tijmenbruggeman Apr 8, 2025
3589bd7
Add tests for picture
tijmenbruggeman Apr 8, 2025
1ef5d1b
Will return array
tijmenbruggeman Apr 8, 2025
4f4cfee
Change conditions to use non-constants
tijmenbruggeman Apr 8, 2025
4cc595f
Start of integration tests
tijmenbruggeman Apr 8, 2025
61f5d06
Support avif responses
tijmenbruggeman Apr 11, 2025
9150ed1
Remove replace
tijmenbruggeman Apr 13, 2025
2e53783
Remove replace option
tijmenbruggeman Apr 15, 2025
5e1b14f
remove replace from integration tests
tijmenbruggeman Apr 15, 2025
52e28f0
Change conversion settings
tijmenbruggeman Apr 15, 2025
9d30dfd
Remove replace from integration test
tijmenbruggeman Apr 15, 2025
2f2dd06
Remove convert options
tijmenbruggeman Apr 15, 2025
4410781
Remove convert options
tijmenbruggeman Apr 15, 2025
b16dab5
Format
tijmenbruggeman Apr 15, 2025
a6274c1
Change documentation
tijmenbruggeman Apr 15, 2025
957fc48
Rename func to better indicate deleting size
tijmenbruggeman Apr 15, 2025
49c6ae2
Basic conversion test
tijmenbruggeman Apr 15, 2025
01ad256
Improve price explainer
tijmenbruggeman Apr 16, 2025
2585bae
Calculate price for conversion as well
tijmenbruggeman Apr 16, 2025
c2998ad
Convert to static methods
tijmenbruggeman Apr 17, 2025
fc0f471
Add helpers
tijmenbruggeman Apr 17, 2025
e8a50a1
Fix build
tijmenbruggeman Apr 17, 2025
47acdc1
Convert to static
tijmenbruggeman Apr 17, 2025
83757c1
Add zip step
tijmenbruggeman Apr 17, 2025
96504f4
fopen request should be a get
tijmenbruggeman Apr 17, 2025
70c1df1
Format
tijmenbruggeman Apr 17, 2025
3683101
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Apr 17, 2025
7ce9b70
Zip cmd
tijmenbruggeman Apr 17, 2025
5952d13
Add xdebug for wordpress runner
tijmenbruggeman Apr 17, 2025
f2e1446
Use ABS_PATH instead of get_home_dir
tijmenbruggeman Apr 17, 2025
2cb493d
Remove get_home_path
tijmenbruggeman Apr 17, 2025
612c1b8
Inject allowed domains and base_dir
tijmenbruggeman Apr 17, 2025
dfeb921
Fix test
tijmenbruggeman Apr 17, 2025
111c3d8
Remove trailing commas
tijmenbruggeman Apr 17, 2025
88c48c0
Post unused
tijmenbruggeman Apr 17, 2025
8513811
Different method for retrieving image url
tijmenbruggeman Apr 17, 2025
2caf4d7
Turn off conversion in bulk spec
tijmenbruggeman Apr 17, 2025
d345c49
Format style
tijmenbruggeman Apr 17, 2025
36b296e
format
tijmenbruggeman Apr 17, 2025
5cf4c65
format
tijmenbruggeman Apr 17, 2025
77f9cdd
Resolve int tests
tijmenbruggeman Apr 17, 2025
5bf396d
Change check method
tijmenbruggeman Apr 17, 2025
b7d9731
Skip welcome guide
tijmenbruggeman Apr 17, 2025
93d58a7
Use get_convert_format_option in get_conversion_enabled
tijmenbruggeman Apr 24, 2025
f045631
Add settings
tijmenbruggeman Apr 24, 2025
b2c9ffe
Compress and convert in one action
tijmenbruggeman Apr 24, 2025
219c8ee
Show conversions in summary
tijmenbruggeman Apr 24, 2025
ccd8b3d
Show statistics and convert button
tijmenbruggeman Apr 24, 2025
f4716f6
Show statistics for conversion
tijmenbruggeman Apr 25, 2025
41245e9
Admin styling
tijmenbruggeman Apr 25, 2025
6c07c37
Change to public methods
tijmenbruggeman Apr 25, 2025
2599c65
Replace picture by srcset and server side checking
tijmenbruggeman Apr 28, 2025
a111449
formatting
tijmenbruggeman Apr 28, 2025
c7d295f
format: yoda cond
tijmenbruggeman Apr 28, 2025
d3c2690
Ensure third param
tijmenbruggeman May 3, 2025
026a3ef
Use srcset instead of picture element
tijmenbruggeman May 3, 2025
b3758ba
format
tijmenbruggeman May 3, 2025
c85699d
Format settings
tijmenbruggeman May 3, 2025
fb8e5d4
Format
tijmenbruggeman May 3, 2025
1bfb534
Format
tijmenbruggeman May 3, 2025
40496f6
Add await to goto
tijmenbruggeman May 3, 2025
8217fed
Fix typo
tijmenbruggeman May 3, 2025
0fb8169
Remove trailing komma
tijmenbruggeman May 3, 2025
7b55ec3
No ??
tijmenbruggeman May 3, 2025
2ce9e68
Format
tijmenbruggeman May 4, 2025
5255e8c
Get mimetypes from settings
tijmenbruggeman May 5, 2025
70a8c4f
Double return + test fix
tijmenbruggeman May 8, 2025
e4a5faa
Typos
tijmenbruggeman May 19, 2025
7f0f3a3
Change to picture element implementation
tijmenbruggeman Jun 3, 2025
385b014
Format
tijmenbruggeman Jun 3, 2025
315db7d
Format
tijmenbruggeman Jun 3, 2025
c4f1827
Fix integration test
tijmenbruggeman Jun 3, 2025
f323ef0
Use array instead of []
tijmenbruggeman Jun 3, 2025
9256b08
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Jun 12, 2025
83e0621
Use statistics for compressed total size
tijmenbruggeman Jun 12, 2025
dafe6b2
Reduce MR size
tijmenbruggeman Jun 12, 2025
20a72c9
Use raw string instead of DOMDocument
tijmenbruggeman Jun 12, 2025
01455df
Remove debug script
tijmenbruggeman Jun 12, 2025
aa643fb
Format
tijmenbruggeman Jun 12, 2025
1851479
Format
tijmenbruggeman Jun 12, 2025
be512d0
Rename statistic to compressed
tijmenbruggeman Jun 12, 2025
c95e673
Add additiona table cells to match table size
tijmenbruggeman Jun 12, 2025
e826db0
Only show columns when conversion is enabled
tijmenbruggeman Jun 12, 2025
cbc7b03
Format
tijmenbruggeman Jun 12, 2025
d922716
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Jun 12, 2025
fc288e3
Add emailadress to limit reached response
tijmenbruggeman Jun 16, 2025
99a0c12
Remove unneeded force click
tijmenbruggeman Jun 16, 2025
788cfdd
Check if resize_options has valid values
tijmenbruggeman Jun 16, 2025
6f0be87
It is possible to resize on only height or width
tijmenbruggeman Jun 17, 2025
a81c298
Add a more performant way of clearing the media library
tijmenbruggeman Jun 17, 2025
8c3f212
Explicitly go to list view for attachments
tijmenbruggeman Jun 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ node_modules/
/playwright/.cache/
test/integration/.auth/user.json
artifacts/
.phpunit.result.cache
26 changes: 13 additions & 13 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9003
}
]
}
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "XDebug PHPUnit",
"type": "php",
"request": "launch",
"port": 9003
}
]
}
24 changes: 18 additions & 6 deletions src/class-tiny-bulk-optimization.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static function get_optimization_statistics( $settings, $result = null )
$stats = array();
$stats['uploaded-images'] = 0;
$stats['optimized-image-sizes'] = 0;
$stats['available-unoptimised-sizes'] = 0;
$stats['available-unoptimized-sizes'] = 0;
$stats['optimized-library-size'] = 0;
$stats['unoptimized-library-size'] = 0;
$stats['available-for-optimization'] = array();
Expand Down Expand Up @@ -99,6 +99,8 @@ private static function wpdb_retrieve_images_and_metadata( $start_id ) {
private static function populate_optimization_statistics( $settings, $result, $stats ) {
$active_sizes = $settings->get_sizes();
$active_tinify_sizes = $settings->get_active_tinify_sizes();
$conversion_enabled = $settings->get_conversion_enabled();

for ( $i = 0; $i < sizeof( $result ); $i++ ) {
$wp_metadata = unserialize( (string) $result[ $i ]['meta_value'] );
$tiny_metadata = unserialize( (string) $result[ $i ]['tiny_meta_value'] );
Expand All @@ -114,18 +116,28 @@ private static function populate_optimization_statistics( $settings, $result, $s
$active_tinify_sizes
);
$image_stats = $tiny_image->get_statistics( $active_sizes, $active_tinify_sizes );

$stats['uploaded-images']++;
$stats['available-unoptimised-sizes'] += $image_stats['available_unoptimized_sizes'];
$stats['optimized-image-sizes'] += $image_stats['image_sizes_optimized'];
$stats['optimized-library-size'] += $image_stats['optimized_total_size'];
if ( $conversion_enabled ) {
$stats['available-unoptimized-sizes'] +=
$image_stats['available_unconverted_sizes'];
$stats['optimized-image-sizes'] +=
$image_stats['image_sizes_converted'];
} else {
$stats['available-unoptimized-sizes'] +=
$image_stats['available_uncompressed_sizes'];
$stats['optimized-image-sizes'] +=
$image_stats['image_sizes_compressed'];
}
$stats['optimized-library-size'] += $image_stats['compressed_total_size'];
$stats['unoptimized-library-size'] += $image_stats['initial_total_size'];
if ( $image_stats['available_unoptimized_sizes'] > 0 ) {
if ( $image_stats['available_uncompressed_sizes'] > 0 ) {
$stats['available-for-optimization'][] = array(
'ID' => $result[ $i ]['ID'],
'post_title' => $result[ $i ]['post_title'],
);
}
}
}// End for().
return $stats;
}
}
40 changes: 27 additions & 13 deletions src/class-tiny-compress-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
}

class Tiny_Compress_Client extends Tiny_Compress {

private $last_error_code = 0;
private $last_message = '';
private $proxy;
Expand Down Expand Up @@ -72,7 +73,6 @@ protected function validate() {
$this->set_request_options( \Tinify\Tinify::getClient( \Tinify\Tinify::ANONYMOUS ) );
\Tinify\Tinify::getClient()->request( 'get', '/keys/' . $this->get_key() );
return true;

} catch ( \Tinify\Exception $err ) {
$this->last_error_code = $err->status;

Expand All @@ -88,7 +88,7 @@ protected function validate() {
}
}

protected function compress( $input, $resize_opts, $preserve_opts ) {
protected function compress( $input, $resize_opts, $preserve_opts, $convert_opts ) {
try {
$this->last_error_code = 0;
$this->set_request_options( \Tinify\Tinify::getClient() );
Expand All @@ -103,25 +103,39 @@ protected function compress( $input, $resize_opts, $preserve_opts ) {
$source = $source->preserve( $preserve_opts );
}

$result = $source->result();

$compress_result = $source->result();
$meta = array(
'input' => array(
'size' => strlen( $input ),
'type' => $result->mediaType(),
'type' => Tiny_Helpers::get_mimetype( $input ),
),
'output' => array(
'size' => $result->size(),
'type' => $result->mediaType(),
'width' => $result->width(),
'height' => $result->height(),
'ratio' => round( $result->size() / strlen( $input ), 4 ),
'size' => $compress_result->size(),
'type' => $compress_result->mediaType(),
'width' => $compress_result->width(),
'height' => $compress_result->height(),
'ratio' => round( $compress_result->size() / strlen( $input ), 4 ),
),
);

$buffer = $result->toBuffer();
return array( $buffer, $meta );
$buffer = $compress_result->toBuffer();
$result = array( $buffer, $meta, null );

if ( isset( $convert_opts['convert'] ) && true == $convert_opts['convert'] ) {
$convert_to = $convert_opts['convert_to'];
$convert_source = $source->convert( array(
'type' => $convert_to,
) );
$convert_result = $convert_source->result();
$meta['convert'] = array(
'type' => $convert_result->mediaType(),
'size' => $convert_result->size(),
);
$convert_buffer = $convert_result->toBuffer();
$result = array( $buffer, $meta, $convert_buffer );
}

return $result;
} catch ( \Tinify\Exception $err ) {
$this->last_error_code = $err->status;

Expand All @@ -130,7 +144,7 @@ protected function compress( $input, $resize_opts, $preserve_opts ) {
get_class( $err ),
$err->status
);
}// End try().
} // End try().
}

public function create_key( $email, $options ) {
Expand Down
45 changes: 37 additions & 8 deletions src/class-tiny-compress-fopen.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ protected function validate() {
}
}

protected function compress( $input, $resize_opts, $preserve_opts ) {
protected function compress( $input, $resize_opts, $preserve_opts, $convert_opts ) {
$params = $this->request_options( 'POST', $input );
list($details, $headers, $status_code) = $this->request( $params );

Expand Down Expand Up @@ -133,7 +133,7 @@ protected function compress( $input, $resize_opts, $preserve_opts ) {
$meta = array(
'input' => array(
'size' => strlen( $input ),
'type' => $headers['content-type'],
'type' => Tiny_Helpers::get_mimetype( $input ),
),
'output' => array(
'size' => strlen( $output ),
Expand All @@ -144,9 +144,36 @@ protected function compress( $input, $resize_opts, $preserve_opts ) {
),
);

return array( $output, $meta );
}
$convert = null;

if ( isset( $convert_opts['convert'] ) && true === $convert_opts['convert'] ) {
$convert_to = $convert_opts['convert_to'];
$convert_params = $this->request_options(
'POST',
array(
'convert' => array(
'type' => $convert_to,
),
),
array( 'Content-Type: application/json' )
);

list($convert_output, $convert_headers) = $this->request(
$convert_params,
$output_url
);
$meta['convert'] = array(
'type' => $convert_headers['content-type'],
'size' => strlen( $convert_output ),
);
$convert = $convert_output;

}

$result = array( $output, $meta, $convert );

return $result;
}
private function request( $params, $url = Tiny_Config::SHRINK_URL ) {
$context = stream_context_create( $params );
$request = fopen( $url, 'rb', false, $context );
Expand Down Expand Up @@ -188,8 +215,10 @@ private function request( $params, $url = Tiny_Config::SHRINK_URL ) {
$response = stream_get_contents( $request );
fclose( $request );

if ( isset( $headers['content-type'] ) &&
substr( 'application/json' == $headers['content-type'], 0, 16 ) ) {
if (
isset( $headers['content-type'] ) &&
substr( 'application/json' == $headers['content-type'], 0, 16 )
) {
$response = $this->decode( $response );
}

Expand Down Expand Up @@ -221,11 +250,11 @@ private function request_options( $method, $body = null, $headers = array() ) {
return array(
'http' => array(
'method' => $method,
'header' => array_merge( $headers, array(
'header' => array_merge($headers, array(
'Authorization: Basic ' . base64_encode( 'api:' . $this->api_key ),
'User-Agent: ' . self::identifier(),
'Content-Type: multipart/form-data',
) ),
)),
'content' => $body,
'follow_location' => 0,
'max_redirects' => 1, // Necessary for PHP 5.2
Expand Down
67 changes: 57 additions & 10 deletions src/class-tiny-compress.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

abstract class Tiny_Compress {

const KEY_MISSING = 'Register an account or provide an API key first';
const FILE_MISSING = 'File does not exist';
const WRITE_ERROR = 'No permission to write to file';
Expand All @@ -43,7 +44,7 @@ public static function create( $api_key, $after_compress_callback = null ) {
public static function estimate_cost( $compressions, $usage ) {
return round(
self::compression_cost( $compressions + $usage ) -
self::compression_cost( $usage ),
self::compression_cost( $usage ),
2
);
}
Expand Down Expand Up @@ -80,7 +81,7 @@ public function get_status() {
if ( $err->get_status() == 404 ) {
$message = 'The key that you have entered is not valid';
} else {
list( $message ) = explode( ' (HTTP', $err->getMessage(), 2 );
list($message) = explode( ' (HTTP', $err->getMessage(), 2 );
}
}

Expand All @@ -92,7 +93,21 @@ public function get_status() {
);
}

public function compress_file( $file, $resize_opts = array(), $preserve_opts = array() ) {
/**
* Compresses a single file
*
* @param [type] $file
* @param array $resize_opts
* @param array $preserve_opts
* @param array{ convert: bool, convert_to: string } conversion options
* @return void
*/
public function compress_file(
$file,
$resize_opts = array(),
$preserve_opts = array(),
$convert_opts = array()
) {
if ( $this->get_key() == null ) {
throw new Tiny_Exception( self::KEY_MISSING, 'KeyError' );
}
Expand All @@ -110,28 +125,55 @@ public function compress_file( $file, $resize_opts = array(), $preserve_opts = a
}

try {
list( $output, $details ) = $this->compress(
file_get_contents( $file ),
$file_data = file_get_contents( $file );

list($output, $details, $convert_output ) = $this->compress(
$file_data,
$resize_opts,
$preserve_opts
$preserve_opts,
$convert_opts
);
} catch ( Tiny_Exception $err ) {
$this->call_after_compress_callback();
throw $err;
}

$this->call_after_compress_callback();
file_put_contents( $file, $output );
try {
file_put_contents( $file, $output );
} catch ( Exception $e ) {
throw new Tiny_Exception( $e->getMessage(), 'FileError' );
}

if ( $convert_output ) {
$converted_filepath = Tiny_Helpers::replace_file_extension(
$details['convert']['type'],
$file
);

try {
file_put_contents( $converted_filepath, $convert_output );
} catch ( Exception $e ) {
throw new Tiny_Exception( $e->getMessage(), 'FileError' );
}
$details['convert']['path'] = $converted_filepath;
}

if ( $resize_opts ) {
$details['output']['resized'] = true;
}

$this->call_after_compress_callback();

return $details;
}

protected abstract function validate();
protected abstract function compress( $input, $resize_options, $preserve_options );
protected abstract function compress(
$input,
$resize_options,
$preserve_options,
$convert_opts
);

protected static function identifier() {
return 'WordPress/' . Tiny_Plugin::wp_version() . ' Plugin/' . Tiny_Plugin::version();
Expand All @@ -150,7 +192,12 @@ private static function needs_resize( $file, $resize_options ) {

list($width, $height) = getimagesize( $file );

return ( $width > $resize_options['width'] || $height > $resize_options['height'] );
$should_resize_width = isset( $resize_options['width'] ) &&
$width > $resize_options['width'];
$should_resize_height = isset( $resize_options['height'] ) &&
$height > $resize_options['height'];

return $should_resize_width || $should_resize_height;
}

private static function compression_cost( $total ) {
Expand Down
Loading