Opened 6 weeks ago
Closed 5 weeks ago
#64804 closed defect (bug) (fixed)
REST API: Add finalize endpoint to `WP_REST_Attachments_Controller` for client-side media processing
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.0 | Priority: | normal |
| Severity: | normal | Version: | trunk |
| Component: | REST API | Keywords: | has-patch has-unit-tests commit |
| Focuses: | Cc: |
Description (last modified by )
When client-side media processing is active, the editor handles image resizing, thumbnail generation, and format conversion in the browser using WebAssembly. This bypasses the wp_generate_attachment_metadata filter that normally fires during wp_generate_attachment_metadata() in wp-admin/includes/image.php.
Many plugins rely on this filter to post-process uploads — for example, adding watermarks, syncing to a CDN, generating custom image sizes, or updating external indexes. When client-side processing is active, these plugins silently stop working because the filter never fires.
Proposed solution
Add a POST /wp/v2/media/{id}/finalize REST API endpoint to WP_REST_Attachments_Controller. This endpoint:
- Retrieves the current attachment metadata
- Re-applies the
wp_generate_attachment_metadatafilter with context'update' - Saves the (potentially modified) metadata back
- Returns the updated attachment object
The endpoint is only registered when wp_is_client_side_media_processing_enabled() returns true, so it has zero impact on sites not using client-side processing.
How it works
The client-side media pipeline calls this endpoint after all operations (upload, thumbnail sideloads, scaled image sideload) are complete. No new hooks are needed — the existing wp_generate_attachment_metadata filter is reused. Plugins that already hook into this filter will work without modification; they can distinguish client-side finalization by checking the $context parameter:
<?php add_filter( 'wp_generate_attachment_metadata', function( $metadata, $attachment_id, $context ) { if ( 'update' === $context ) { // Called from finalize endpoint after client-side processing. my_plugin_process_attachment( $attachment_id, $metadata ); } return $metadata; }, 10, 3 );
Permissions
The endpoint reuses edit_media_item_permissions_check(), consistent with the existing sideload and edit endpoints. A user must have permission to edit the attachment.
Related
- Gutenberg PR (hooks + JS pipeline): https://github.com/WordPress/gutenberg/pull/74913
- Gutenberg issue: https://github.com/WordPress/gutenberg/issues/74358
- Core PR: https://github.com/WordPress/wordpress-develop/pull/11168
- Parent ticket (client-side media processing): #62243
- Previous Core backport PR: https://github.com/WordPress/wordpress-develop/pull/10868
Change History (7)
This ticket was mentioned in PR #11168 on WordPress/wordpress-develop by @adamsilverstein.
6 weeks ago
#1
- Keywords has-unit-tests added
#3
@
6 weeks ago
@adamsilverstein Would it not make sense to just call wp_generate_attachment_metadata() after each image size is sideloaded? That way the metadata will be sure to be created even when no finalize request is made (e.g. the client goes offline). Or is the concern that re-generating the metadata each time would cause relevant plugins to do redundant work?
#4
@
6 weeks ago
My main goal is to match the existing core hook behavior which I believe means firing once at the end of the process. Some plugins may expect all sub sizes to be in place. Worth reviewing how plugins actually use this hook.
## Summary
POST /wp/v2/media/{id}/finalizeREST API endpoint toWP_REST_Attachments_Controllerwp_generate_attachment_metadatafilter with context'update'after client-side media processing completeswp_is_client_side_media_processing_enabled()returns true## Context
When client-side media processing handles image uploads (resizing, thumbnail generation, format conversion), server-side WordPress hooks like
wp_generate_attachment_metadataare bypassed. This endpoint is called after all client-side operations (upload, thumbnail generation, sideloads) are complete, re-triggering the filter so plugins continue to work.### Hook usage example
add_filter( 'wp_generate_attachment_metadata', function( $metadata, $attachment_id, $context ) { // $context is 'update' when called from the finalize endpoint. if ( 'update' === $context ) { // Add watermark, sync to CDN, generate custom sizes, etc. my_plugin_process_attachment( $attachment_id, $metadata ); } return $metadata; }, 10, 3 );## Changes
class-wp-rest-attachments-controller.phpfinalizeroute registration,finalize_item_permissions_check(), andfinalize_item()methodsrest-attachments-controller.php(tests)rest-schema-setup.phpwp-api-generated.js## Test plan
wp_generate_attachment_metadatafilter fires with context'update'## Related