Skip to content

Conversation

@mitogh
Copy link
Member

@mitogh mitogh commented Feb 4, 2022

Summary

Creates the JPEG version of the WebP images and store the references into _wp_attachment_backup_sizes similar to image transformations in order to take advantage of benefits from WP Core like files removed automatically by WordPress and uploaded into CDNs

Fixes:

Relevant technical choices

The changes introduced in this PR replicate the same behavior from WordPress core to store edited versions of additional mime types and the additional mime types themselves in the meta _wp_attachment_backup_sizes.

Checklist

  • PR has either [Focus] or Infrastructure label.
  • PR has a [Type] label.
  • PR has a milestone or the no milestone label.

The property is added to every single image size available,
each value inside the properties array is a reference to
a different image mime type.

If the image size was created as a JPEG version a WebP version
would be created, if a WebP version exists a JPEG version
would be created instead based on the details from ticket #142

This mechanism would allow extending the sources properties for
more additional mime types.

The value for each mime type is an associative array with `file` as
the only property for now. And the plan is to include more meaningful
properties such as `filesize` to a different mime type.
Make sure file follows the standard from `phpcs`
Prevent to use numeric indexes for each image size, instead use the
correct image name.
In this case the `_wp_attached_file` needs to be used in order
to gain any update to the image, instead of using the original
image for any subsize.
Create an store the backup images for all the subsizes
and supported image sizes.
Make sure the file is formatted properly.
@mitogh mitogh added this to the 1.0.0-beta.1 milestone Feb 4, 2022
@mitogh mitogh self-assigned this Feb 4, 2022
@mitogh mitogh added the [Type] Feature A new feature within an existing module label Feb 4, 2022
@mitogh mitogh marked this pull request as draft February 4, 2022 23:41
}

// TODO: Store the file size of the created image.
// $image['filesize'] = filesize( $image['path'] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of storing this for all sizes, this is useful for performance analysis and potentially other things.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. for now would keep the TODO, and we can came back to this once we start working on:

$backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
$backup_sizes = is_array( $backup_sizes ) ? $backup_sizes : array();

foreach ( webp_uploads_get_image_sizes() as $size => $properties ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of trying to recreate sizes, can we just copy the meta 'sizes'?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can certainly do that but aside from that, we need to know if the newly created image should be cropped or not and that's the main reason why we need this function.

I think something we can do at the top of the foreach loop to make sure this $size exists in the original metadata before creating a targeted image instead.

Copy link
Member Author

@mitogh mitogh Feb 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is already the case on this line 😃

if ( empty( $metadata['sizes'][ $size ] ) || ! is_array( $metadata['sizes'][ $size ] ) ) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I noticed in testing when I uploading a very large image, I got a jpeg created with a -scaled suffix, then all of the generated .webp sub size files also included the -scaled suffix:

image

In addition, I noticed there was no full sized .webp created (that is {filename}-scaled.webp) - we need that full sized file for the image markup (only applies when you upload images larger than big_image_size_threshold)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for testing and for the feedback. Some follow-up comments.

One thing I noticed in testing when I uploading a very large image, I got a jpeg created with a -scaled suffix, then all of the generated .webp sub size files also included the -scaled suffix.

Great point mostly because this is created out of the scaled version. But let me fix this one :)

In addition, I noticed there was no full sized .webp created (that is {filename}-scaled.webp) - we need that full sized file for the image markup (only applies when you upload images larger than big_image_size_threshold)

Actually, this follows the same logic applied here #122 and #143 this behavior would go against this solved issue, as this scaled version is the full size image. Based on the issue scaled and full size always remains in the original image format. (not converted)

Let me know if you think we should change it instead.

mitogh and others added 2 commits February 9, 2022 15:35
Update the logic for hwo the additional mime types were created
in order to allow image edits to take effect, when an image is
cropped, flipped or rotated, those transformations are reflected
into the additional mime types.
This would allow to quickly locate `webp` or a defined extension
images and split between `-orig` images suffix as well.
Prevent namespace collisions when using the function
from within the plugin.
The callback was passed as a parameter, no change
is placed on the callback so we can safely return it.
This was due in some cases when restoring an image and editing
it again, the image would result in not executing correctly
due the meta was the same as before and the update callback was
not executed, instead we are moving this work into an ajax callback.
@mitogh mitogh marked this pull request as ready for review February 15, 2022 14:21
}

// All subsizes are created out of the `file` property and not the original image.
$file = get_attached_file( $attachment_id, true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use get_attached_file here instead of the original image, is this to use the -scaled image?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct any edits to an image are performed to the scaled version (if present) by WordPress so any subsize should be based out of the modified image and not the original image instead.

The original image is only used to create the scaled version if not required it would be used as the attached file instead.

// The file already exists as part of the backup sizes in the same key.
if ( array_key_exists( $key, $backup_sizes ) ) {
// This case would remove the file if is an edited version.
if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

unset( $backup_sizes[ $key ] );
}

// Editor needs to be recreated every time as there is not flush() or clear() function that can be used after we created an image.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try$editor->__destruct (or even just $editor = null which should trigger __destruct)? For both Imagick and GD this should clear the image.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, let me try that thanks for the suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually calling __destruct seems like the same as creating a new variable altogether as essentially this is called by the engine itself so, for now, will keep it like that.

One thing that I've noted out of the class WP_Image_Editor is the fact that has a property get_size but not a set_size which would be the one we can use here to reset the size to the appropriate dimensions. Any thoughts on that one ?

* @since 1.0.0
*/

add_filter( 'wp_generate_attachment_metadata', 'webp_uploads_create_images_with_additional_mime_types', 10, 3 );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating the additional mime types immediately might fail because it will double execution time, right? I can test this locally, but I think this might break uploads for some users.

Can we instead add an immediately one-time executing cron callback (wp_schedule_single_event)? that way the images will get generated on the next callback as part of a separate request?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree I'm going to explore this as part of enchantment on top of this PR as you describe has become quite large at this point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I just updated this PR to handle this case.

);

/**
* An array representing all the valid mime types where multiple images would be created if
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure that by returning only webp in the array only webp images are generated - that is, the uploads would work the way they do now in the plugin.

Although we decided this shouldn't be the default approach, it may turn out to be the best approach for most users, and over time it may become our choice for default. Code wise, we should be able to keep the existing plugin approach, but only use it in the case when a single mime type is specified.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point.

Let me think about how I can go about this use case when only a single mime type is present either JPEG or WebP so I can update this PR accordingly. Another use case we need to consider is the fact that due this is filterable it can be empty as well. Under that scenario we can either:

  • Generate all subsizes with the same mime-type as the original image regardless if this filter is empty
  • Not generate any subsize to follow what was specified on the filter.

It feels like the last point would be the way to go for consistency but let me know your thoughts on that one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this one and added a TODO to handle this last point.

@adamsilverstein
Copy link
Member

@mitogh this looks good so far, I left some small comments and questions.

This PR has grown quite large because in addition to handling image generation you are supporting image edit and restore. I realize these are related because of the data storage, but still wonder if we could break them out so we can focus solely on the image generation and meta data storage. Once we land that, we can tackle the edit/restore bits in a separate PR.

THe hash extraction ahs been moved into a custom function to avoid
confusion and bring clarity when used.
Remove the suffix:

- scaled
- rotated
- imagifyresized

From the file names if present.
The logic to handle the case for image edits like: rotation, flip
and crop would be handled in a separate PR.

This PR addresses the concern for #142 and #150.
The result of the condition is always a boolean, so this
section can be simplified with a single line instead.
Additional images would be created during a cron execution,
removing any workload from the initial request.

Tests where fixed and some new ones where introduced.
@felixarntz
Copy link
Member

@mitogh @adamsilverstein I think the approach in #147 makes more sense than this one. _wp_attachment_backup_sizes is used for edited/cropped versions of an image, while the sizes field of the regular image metadata is what is being used for the registered sizes. Since we are focusing on generating the sub-sizes in progressive image formats, I think our data should be in the sizes array.

Regarding backward-compatibility, I don't think the approach here justifies going with it given the unexpected data storage, specifically for the following reasons:

  • It shouldn't be too hard deleting the "custom" sub-sizes generated in the approach from Create image sub-sizes in additional MIME types using sources for storage. #147, we just have to use the delete_attachment action to delete any of those image files that exist.
  • Regarding CDNs, that indeed is a valid concern. However, we also don't break anything in these plugins or WordPress core with the sources implementation in Create image sub-sizes in additional MIME types using sources for storage. #147, and the eventual dev note post about this feature in WordPress core should include guidance on how to update such type of plugins to support the new sources field. It's okay to introduce a new API for plugins to use, and since we're adding a new feature to WordPress core, it's reasonable to expect plugin developers that want to support the feature to adopt the new "API" (which here really is just newly stored data).

@mitogh
Copy link
Member Author

mitogh commented Feb 17, 2022

@mitogh mitogh closed this Feb 17, 2022
@mitogh mitogh deleted the feature/142-multiple-mimes-for-image-sizes-with-backup branch February 25, 2022 19:15
@felixarntz felixarntz added the no milestone PRs that do not have a defined milestone for release label Mar 7, 2022
@felixarntz felixarntz removed this from the 1.0.0-beta.1 milestone Mar 7, 2022
@mitogh mitogh mentioned this pull request Mar 7, 2022
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no milestone PRs that do not have a defined milestone for release [Type] Feature A new feature within an existing module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants