﻿id	summary	reporter	owner	description	type	status	priority	milestone	component	version	severity	resolution	keywords	cc	focuses
65549	Media: Sideload animated GIF → video companions and clean them up on delete	adamsilverstein	adamsilverstein	"== Summary ==

Opaque animated GIFs are large for the motion they encode. With client-side media processing enabled, the editor transcodes an animated GIF to a web-safe video (via WebCodecs) and sideloads both the converted video and a static first-frame poster as companion files of the GIF attachment.

This change adds the '''server-side''' support for that flow: it teaches the sideload REST route to accept the two companion sizes, and it cleans the companions up when the attachment is deleted.

== Use case / steps to reproduce ==

1. Enable client-side media processing.
2. Upload an opaque animated GIF in the block editor.
3. The editor converts it to a web-safe video and sideloads the video plus a first-frame poster against the same attachment; the inserted block is switched to the core/video block's ""GIF"" variation, which serializes a normal `<video autoplay loop muted playsinline>` and renders natively on the front end with no render-time filtering. The author can restore the original GIF from the block toolbar.
4. Inspect `_wp_attachment_metadata` and the uploads directory, then delete the attachment.

'''Expected:''' the attachment metadata records `animated_video` and `animated_video_poster`, both companion files sit next to the GIF on disk, and deleting the attachment removes them.

'''Actual (before this change):''' the sideload route rejects the `animated-video` / `animated-video-poster` sizes, and — were the companions stored — they would linger on disk after the attachment is deleted, because core's `wp_delete_attachment_files()` only tracks `original_image`.

Transparent animated GIFs are not converted (a `<video>` cannot reproduce GIF transparency), so they have no companion; the cleanup hook simply skips any companion key that isn't recorded.

== Proposed change ==

* Accept two new sideload sizes on `/wp/v2/media/<id>/sideload`:
 * `animated-video` — the converted MP4/WebM, written to `$metadata['animated_video']`.
 * `animated-video-poster` — the static first-frame JPEG, written to `$metadata['animated_video_poster']`.
 Both are written to their own metadata keys without touching `original_image` or `sizes[]`.
* Add `wp_get_attachment_animated_gif_companion_path( $attachment_id, $meta_key )` — rebuilds the absolute companion path from the attachment's own (trusted) directory plus only `wp_basename()` of the recorded value, so the stored metadata can never reference another directory.
* Add `wp_delete_attachment_animated_gif_video( $post_id )` on `delete_attachment` — removes both companions via `wp_delete_file_from_directory( $path, $uploads['basedir'] )`, which enforces the realpath / uploads-basedir guard.

== Patch ==

The patch is available as wordpress-develop PR [https://github.com/WordPress/wordpress-develop/pull/12005 #12005], a server-only backport of Gutenberg [https://github.com/WordPress/gutenberg/pull/78410 #78410]. The block variation, store actions, WebCodecs worker, and editor UI ship through the normal Gutenberg → Core package sync.

Files touched:

* `src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php` — adds `animated-video` and `animated-video-poster` to the sideload `image_size` enum and two `sideload_item()` branches that record the basenames.
* `src/wp-includes/media.php` — new `wp_get_attachment_animated_gif_companion_path()` helper and `wp_delete_attachment_animated_gif_video()` cleanup callback.
* `src/wp-includes/default-filters.php` — registers the `delete_attachment` hook.
* `tests/phpunit/tests/media/wpDeleteAttachmentAnimatedGifVideo.php` and `tests/phpunit/tests/rest-api/rest-attachments-controller.php` — coverage.
* `tests/qunit/fixtures/wp-api-generated.js` — regenerated for the new enum values.

== Tests ==

* `wpDeleteAttachmentAnimatedGifVideo.php` — path resolution, directory-traversal guard, non-string guard, both companions deleted, video-only deletion, and the no-companion no-op (transparent GIFs).
* `rest-attachments-controller.php` — the sideload route accepts the new sizes and writes the corresponding metadata keys.
"	defect (bug)	reviewing	normal	Awaiting Review	Media		normal		has-patch has-unit-tests has-test-info		
