close
Skip to content

Media uploading - declare convert_format as boolean arg on sideload route#77565

Merged
adamsilverstein merged 1 commit into
trunkfrom
fix/heic-convert-format-sideload-arg
Apr 23, 2026
Merged

Media uploading - declare convert_format as boolean arg on sideload route#77565
adamsilverstein merged 1 commit into
trunkfrom
fix/heic-convert-format-sideload-arg

Conversation

@adamsilverstein
Copy link
Copy Markdown
Member

@adamsilverstein adamsilverstein commented Apr 22, 2026

What

Declares convert_format as a boolean arg on the /wp/v2/media/<id>/sideload route so REST coerces the incoming value before it reaches the handler.

Why

The sideload handler reads $request['convert_format'] at class-gutenberg-rest-attachments-controller.php:548 and the client sends convert_format: false in additionalData when uploading a HEIC companion. Because the arg was not declared, multipart/form-data delivered the value as the string "false", which evaluates truthy, so if ( ! $request['convert_format'] ) never fired and add_filter( 'image_editor_output_format', '__return_empty_array', 100 ) was skipped.

With the filter skipped, wp_unique_filename() retained the default HEIC→JPEG output-format mapping and ran its alt-extension collision check — which bumped the .heic original to -1 even though the JPEG derivative stayed at no suffix. On subsequent uploads of the same HEIC, the two filenames drifted further apart (HEIC -3, JPEG -2).

Declaring the arg mirrors what is already done for create_item's endpoint args and lets REST coerce "false" → PHP false. The filter fires, alt-ext is skipped during the sideload, and the HEIC and JPEG end up sharing the same numeric suffix on every upload.

How

  • lib/media/class-gutenberg-rest-attachments-controller.php — add the convert_format boolean arg (default true) to the sideload route's args array, alongside generate_sub_sizes.
  • phpunit/media/class-gutenberg-rest-attachments-controller-test.php — two new tests:
    • test_sideload_route_declares_convert_format_boolean asserts the route schema.
    • test_sideload_convert_format_false_suppresses_alt_ext_suffix simulates the HEIC-companion flow (PNG stand-in + PNG→JPEG mapping) and verifies the companion keeps the shared basename with no numeric suffix.

Both tests fail on trunk and pass with this change.

Testing instructions

  1. npx wp-env run --env-cwd='wp-content/plugins/gutenberg' wordpress vendor/bin/phpunit -c phpunit.xml.dist --filter 'Gutenberg_REST_Attachments_Controller_Test'
  2. Manually upload the same HEIC twice through the media library; verify the .heic original and .jpg derivative share the same numeric suffix (no suffix the first time, -1 the second, etc.) on the filesystem.

Closes #77564

cc @andrewserong

AI use

I prompted Claude Code to write this PR and reviewed and tested it manually.

…route

The sideload handler reads $request['convert_format'] but the route did
not declare it. Multipart/form-data requests delivered the client's
"false" as a PHP string which evaluates truthy, so the filter that
suppresses server-side format conversion was never added. This caused
HEIC companion originals to get a numeric suffix while the JPEG
derivative had none, and the drift widened on every subsequent upload
of the same HEIC.

Declaring convert_format with type=boolean (default true) lets the REST
layer coerce the string to a PHP bool, matching what is already done
for the create_item path, and incidentally fixes the HEIC/JPEG filename
drift reported against #77506.

Fixes #77564.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@adamsilverstein adamsilverstein self-assigned this Apr 22, 2026
@adamsilverstein adamsilverstein added [Type] Bug An existing feature does not function as intended [Status] In Progress Tracking issues with work in progress [Feature] Client Side Media Media processing in the browser with WASM labels Apr 22, 2026
@adamsilverstein
Copy link
Copy Markdown
Member Author

adamsilverstein commented Apr 22, 2026

I will plan to backport this to WordPress core once client side media is restored to core in WordPress/wordpress-develop#11324

@adamsilverstein adamsilverstein requested review from andrewserong and removed request for spacedmonkey April 22, 2026 16:45
@github-actions
Copy link
Copy Markdown

Flaky tests detected in f0b7ac5.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24790568579
📝 Reported issues:

@adamsilverstein
Copy link
Copy Markdown
Member Author

I verified this fixes the naming issue for the original heic upload. Uploading IMG_4301.HEIC, I see these files:

image

And this meta:

array (
  'width' => 1440,
  'height' => 2560,
  'file' => '2026/04/IMG_4301-scaled.jpg',
  'filesize' => 475361,
  'sizes' =>
  array (
    'thumbnail' =>
    array (
      'width' => 150,
      'height' => 150,
      'file' => 'IMG_4301-150x150.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 6563,
    ),
    'medium' =>
    array (
      'width' => 168,
      'height' => 300,
      'file' => 'IMG_4301-168x300.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 10117,
    ),
    'medium_large' =>
    array (
      'width' => 768,
      'height' => 1365,
      'file' => 'IMG_4301-768x1365.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 151370,
    ),
    'large' =>
    array (
      'width' => 576,
      'height' => 1024,
      'file' => 'IMG_4301-576x1024.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 89131,
    ),
    '1536x1536' =>
    array (
      'width' => 864,
      'height' => 1536,
      'file' => 'IMG_4301-864x1536.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 190404,
    ),
    '2048x2048' =>
    array (
      'width' => 1152,
      'height' => 2048,
      'file' => 'IMG_4301-1152x2048.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 321690,
    ),
    'custom-500' =>
    array (
      'width' => 500,
      'height' => 500,
      'file' => 'IMG_4301-500x500.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 51923,
    ),
    'post-thumbnail' =>
    array (
      'width' => 1568,
      'height' => 2788,
      'file' => 'IMG_4301-1568x2788.jpg',
      'mime-type' => 'image/jpeg',
      'filesize' => 551148,
    ),
  ),
  'image_meta' =>
  array (
    'aperture' => '0',
    'credit' => '',
    'camera' => '',
    'caption' => '',
    'created_timestamp' => '0',
    'copyright' => '',
    'focal_length' => '0',
    'iso' => '0',
    'shutter_speed' => '0',
    'title' => '',
    'orientation' => '0',
    'keywords' =>
    array (
    ),
    'alt' => '',
  ),
  'original' => 'IMG_4301.heic',
  'original_image' => 'IMG_4301.jpg',
)

@adamsilverstein adamsilverstein changed the title Client-side media: declare convert_format as boolean arg on sideload route Media uploading - declare convert_format as boolean arg on sideload route Apr 22, 2026
Copy link
Copy Markdown
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

Looking good, and nice test coverage. Glad it was a simple fix!

Confirmed that the incrementing is now consistent across all files including the heic stored alongside all the generated image sizes:

Image

:shipit:

@adamsilverstein adamsilverstein merged commit 2462ae4 into trunk Apr 23, 2026
52 of 57 checks passed
@adamsilverstein adamsilverstein deleted the fix/heic-convert-format-sideload-arg branch April 23, 2026 01:19
@github-actions github-actions Bot added this to the Gutenberg 23.1 milestone Apr 23, 2026
adamsilverstein added a commit that referenced this pull request Apr 28, 2026
The feature shipping target moved from 7.0 to 7.1 (per #76756). Bring
the architecture explanation and how-to guide up to date with the
post-7.0-cycle state of the code:

- Reposition introductions around 7.1 with 7.0 as the groundwork cycle.
- HEIC/HEIF: document the canvas-based fallback path (createImageBitmap,
  HTMLImageElement+OffscreenCanvas, HEIC container parsing + WebCodecs
  VideoDecoder), JPEG companion file, and isHeicCanvasSupported() gate.
- AVIF: note the wp_prevent_unsupported_mime_type_uploads bypass when
  generate_sub_sizes=false (#76371).
- Batch thumbnail generation via image.copyMemory() / thumbnailImage()
  (#76979).
- Sub-size deduplication via image_size: string|string[] on the sideload
  route (#77036).
- Single VIPS instance via promise caching (#76780).
- Build-output trimming: remove vips-jxl.wasm (#76639), skip non-min
  worker (#76615), skip WASM-inlined sourcemaps (#75993).
- COI: <img> excluded from cross-origin attribute injection (#76618).
- Loosened feature-detection thresholds: 2 cores, 3g (#76616).
- Post-saving lock fix: Save Draft now respects the lock (#76973).
- convert_format declared as boolean on sideload route (#77565).
- Fix incorrect REST index settings list (only image_sizes and
  image_size_threshold are exposed).
- Remove references to client_side_supported_mime_types filter
  (PR #76549 was closed unmerged); state the supported MIME set is
  fixed at CLIENT_SIDE_SUPPORTED_MIME_TYPES.

Refs #75111.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Client Side Media Media processing in the browser with WASM [Status] In Progress Tracking issues with work in progress [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HEIC suffix drift and missing convert_format arg on sideload endpoint

2 participants