Skip to content

VideoPress: Refresh media library processing status via Heartbeat#47358

Open
obenland wants to merge 6 commits intotrunkfrom
add/vidp-175-videopress-media-library-does-not-refresh-the-processing
Open

VideoPress: Refresh media library processing status via Heartbeat#47358
obenland wants to merge 6 commits intotrunkfrom
add/vidp-175-videopress-media-library-does-not-refresh-the-processing

Conversation

@obenland
Copy link
Member

Fixes https://github.com/Automattic/videopress/issues/1220

Proposed changes:

  • Pass videopress_status post meta through wp_prepare_attachment_for_js so the Backbone model knows which videos are still processing.
  • Add a WP Heartbeat integration that sends processing video IDs on each tick and receives their current status from the server.
  • When a video's status becomes complete, refetch the full attachment data via get-attachment AJAX, updating the Backbone model and re-rendering the grid item automatically.

Other information:

  • Have you written new tests for your changes, if applicable?
  • Have you checked the E2E test CI results, and verified that your changes do not break them?
  • Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

Does this pull request change what data or activity we track or use?

No.

Testing instructions:

  • Go to /wp-admin/upload.php (Media Library grid view).
  • Upload a video file that VideoPress will process.
  • Observe the processing icon/thumbnail in the grid.
  • Wait for transcoding to complete on the WordPress.com backend (the XMLRPC callback will update videopress_status to complete).
  • Confirm the grid item updates automatically without a manual page refresh — the processing icon should be replaced by the normal video thumbnail.
  • Confirm that once all videos are complete, the Heartbeat no longer includes videopress_processing_ids in its payload (check Network tab for admin-ajax.php heartbeat requests).

Changelog

Changelog entry included in projects/packages/videopress/changelog/fix-media-library-processing-status-refresh.

Copilot AI review requested due to automatic review settings February 26, 2026 19:46
@obenland obenland added the [Status] Needs Review This PR is ready for review. label Feb 26, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the add/vidp-175-videopress-media-library-does-not-refresh-the-processing branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack add/vidp-175-videopress-media-library-does-not-refresh-the-processing

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: March 3, 2026
    • Code freeze: March 3, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the WP Admin Media Library (grid view) experience for VideoPress uploads by surfacing processing state to the media Backbone models and using WP Heartbeat to refresh items automatically when transcoding completes.

Changes:

  • Expose videopress_status attachment meta in wp_prepare_attachment_for_js so the Media Library JS model knows which items are still processing.
  • Add a Media Library script that sends processing attachment IDs via Heartbeat and, when complete, refetches attachment data to update the grid item.
  • Add a Heartbeat server handler to return current videopress_status values for requested attachment IDs.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.

File Description
projects/packages/videopress/src/js/media-library-poll.js Adds Heartbeat send/tick hooks to poll processing IDs and refresh attachments when complete.
projects/packages/videopress/src/class-attachment-handler.php Includes videopress_status in attachment data prepared for the media JS model.
projects/packages/videopress/src/class-admin-ui.php Enqueues the polling script on upload.php and implements the Heartbeat response payload.
projects/packages/videopress/changelog/fix-media-library-processing-status-refresh Adds changelog entry describing the fix.
Comments suppressed due to low confidence (4)

projects/packages/videopress/src/js/media-library-poll.js:62

  • In the heartbeat-tick handler, if the server reports status === 'complete' for an attachment, this will fire a get-attachment AJAX request on every subsequent heartbeat until the first request returns (because the model’s videopress_status isn’t updated until .done()). Consider marking the attachment as “refreshing” (e.g., a transient attribute) before sending, and clearing it in .always(), to avoid duplicate concurrent refresh requests for the same ID.
		$.each( data.videopress_processing_status, function ( id, status ) {
			const attachment = library.get( id );
			if ( attachment && status === 'complete' ) {
				wp.ajax
					.send( 'get-attachment', {
						data: { id: id },
					} )
					.done( function ( attrs ) {
						attachment.set( attrs );
					} );

projects/packages/videopress/src/class-admin-ui.php:458

  • heartbeat_received() trusts client-provided videopress_processing_ids and loops through them unbounded. To avoid information leaks and potential authenticated DoS via many IDs, please (1) cap the number of IDs processed, (2) ensure the current user can at least edit_post/read_post for each attachment, and (3) consider priming the post/meta cache (e.g., update_meta_cache) before reading videopress_status in a loop to avoid N+1 queries.
	public static function heartbeat_received( $response, $data ) {
		if ( empty( $data['videopress_processing_ids'] ) || ! is_array( $data['videopress_processing_ids'] ) ) {
			return $response;
		}

		$statuses = array();
		foreach ( $data['videopress_processing_ids'] as $id ) {
			$id     = (int) $id;
			$status = get_post_meta( $id, 'videopress_status', true );

projects/packages/videopress/src/class-admin-ui.php:468

  • This adds new server behavior (heartbeat_received) that reads attachment meta based on client input. Since the package has PHPUnit coverage, please add unit tests covering: empty/invalid payloads, capability checks (once added), and that a known attachment ID returns the expected videopress_processing_status mapping.
	public static function heartbeat_received( $response, $data ) {
		if ( empty( $data['videopress_processing_ids'] ) || ! is_array( $data['videopress_processing_ids'] ) ) {
			return $response;
		}

		$statuses = array();
		foreach ( $data['videopress_processing_ids'] as $id ) {
			$id     = (int) $id;
			$status = get_post_meta( $id, 'videopress_status', true );
			if ( $status ) {
				$statuses[ $id ] = $status;
			}
		}

		if ( ! empty( $statuses ) ) {
			$response['videopress_processing_status'] = $statuses;
		}

		return $response;

projects/packages/videopress/src/class-attachment-handler.php:205

  • prepare_attachment_for_js() now exposes videopress_status to the media JS model. Since this is new API surface used by the new polling code, please add a PHPUnit test asserting that when videopress_status meta is set on a video attachment, the prepared JS array includes videopress_status (and omits it when the meta is absent).
			}
			$status = get_post_meta( $post['id'], 'videopress_status', true );
			if ( $status ) {
				$post['videopress_status'] = $status;
			}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jp-launch-control
Copy link

jp-launch-control bot commented Feb 26, 2026

Code Coverage Summary

Coverage changed in 3 files.

File Coverage Δ% Δ Uncovered
projects/packages/videopress/src/utility-functions.php 39/270 (14.44%) -0.74% 2 ❤️‍🩹
projects/packages/videopress/src/class-attachment-handler.php 41/116 (35.34%) 18.68% 0 💚
projects/packages/videopress/src/class-wpcom-rest-api-v2-attachment-videopress-data.php 31/105 (29.52%) 17.14% -18 💚

1 file is newly checked for coverage.

File Coverage
projects/packages/videopress/src/js/media-library-poll.js 28/30 (93.33%) 💚

Full summary · PHP report · JS report

@obenland obenland force-pushed the add/vidp-175-videopress-media-library-does-not-refresh-the-processing branch from 08ed1c4 to ca8932a Compare February 27, 2026 17:37
After uploading a video through the Media Library, the processing
thumbnail persisted even after transcoding completed on the server.
This adds a WP Heartbeat integration that polls for status changes
and refreshes the grid item when transcoding finishes.
Copilot AI review requested due to automatic review settings February 27, 2026 17:49
@obenland obenland force-pushed the add/vidp-175-videopress-media-library-does-not-refresh-the-processing branch from ca8932a to 2ad775f Compare February 27, 2026 17:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

projects/packages/videopress/src/js/test/media-library-poll.js:167

  • Missing test coverage for AJAX error scenarios. The tests only cover successful AJAX responses in the 'fetches attachment data when status becomes complete' test. There should be tests that verify behavior when the get-attachment AJAX request fails, such as network errors or server errors. This is important because the implementation doesn't currently handle these failures.
	it( 'fetches attachment data when status becomes complete', () => {
		const attachment = createAttachment( {
			id: 10,
			type: 'video',
			subtype: 'videopress',
			videopress_status: 'processing',
		} );
		setupLibrary( [ attachment ] );

		const deferred = {
			done: jest.fn( cb => {
				cb( { id: 10, icon: 'new-icon.png' } );
				return deferred;
			} ),
		};
		global.wp.ajax.send.mockReturnValue( deferred );

		loadScript();

		handlers[ 'heartbeat-tick' ](
			{},
			{
				videopress_processing_status: { 10: 'complete' },
			}
		);

		expect( global.wp.ajax.send ).toHaveBeenCalledWith( 'get-attachment', {
			data: { id: '10' },
		} );
		expect( attachment.set ).toHaveBeenCalledWith( { id: 10, icon: 'new-icon.png' } );
	} );

projects/packages/videopress/src/js/media-library-poll.js:64

  • Potential race condition when multiple videos complete simultaneously. If multiple videos finish processing at the same time, multiple get-attachment AJAX calls will be initiated in parallel (lines 56-62). Since there's no queuing or rate limiting, this could result in a burst of AJAX requests. While not necessarily a critical issue, consider implementing request batching or queuing to reduce server load if many videos complete simultaneously.
		$.each( data.videopress_processing_status, function ( id, status ) {
			const attachment = library.get( id );
			if ( attachment && status === 'complete' ) {
				wp.ajax
					.send( 'get-attachment', {
						data: { id: id },
					} )
					.done( function ( attrs ) {
						attachment.set( attrs );
					} );
			}
		} );

projects/packages/videopress/src/class-attachment-handler.php:349

  • The heartbeat_received function does not validate user permissions before returning attachment status information. While WordPress's built-in get-attachment AJAX handler likely has its own permission checks, it's a security best practice to validate at each layer. Consider adding a capability check such as current_user_can('upload_files') to ensure only authorized users can query video processing status. This provides defense-in-depth and makes the security posture explicit in this code.
	public static function heartbeat_received( $response, $data ) {
		if ( empty( $data['videopress_processing_ids'] ) || ! is_array( $data['videopress_processing_ids'] ) ) {
			return $response;
		}

		$statuses = array();
		foreach ( $data['videopress_processing_ids'] as $id ) {
			$id     = (int) $id;
			$status = get_post_meta( $id, 'videopress_status', true );
			if ( $status ) {
				$statuses[ $id ] = $status;
			}
		}

		if ( ! empty( $statuses ) ) {
			$response['videopress_processing_status'] = $statuses;
		}

		return $response;
	}

projects/packages/videopress/src/js/media-library-poll.js:58

  • The JavaScript passes 'id' as a string in line 58, but this inconsistency could cause issues. In line 164 of the test file, the test expects 'id' to be passed as a string ('10'), which matches the implementation. However, it would be more consistent to pass the id as a number since Backbone's library.get() already coerces types. Consider standardizing on one type for clarity.
						data: { id: id },

projects/packages/videopress/src/js/media-library-poll.js:62

  • Missing error handling for the AJAX request. The wp.ajax.send call in line 56-62 only handles the success case with .done(), but doesn't handle failures with .fail(). If the get-attachment AJAX request fails (e.g., network error, server error, unauthorized access), the attachment model won't be updated and there's no retry mechanism or user feedback. Consider adding a .fail() handler to log errors or implement retry logic.
				wp.ajax
					.send( 'get-attachment', {
						data: { id: id },
					} )
					.done( function ( attrs ) {
						attachment.set( attrs );
					} );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ook re-registration

The wp_prepare_attachment_for_js filter passes an array, not a WP_Post.
Update the @param type to match and fix Phan errors.

Restore per-test re-registration of REST API hooks in Data_Test that
was inadvertently removed in f3375fa. WorDBless resets all hooks
between tests, so the rest_attachment_query filter needs to be
re-registered in set_up().
@obenland obenland self-assigned this Feb 27, 2026
Add test coverage for enqueue_media_library_poll. Update the deprecated
videopress_prepare_attachment_for_js wrapper in editor-media-view.php to
match the corrected @param array type.
Copilot AI review requested due to automatic review settings February 27, 2026 18:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions github-actions bot added [Feature] VideoPress A feature to help you upload and insert videos on your site. [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ labels Feb 27, 2026
Set heartbeat to fast (10s) while videos are processing and revert to
standard (60s) when all are complete. Lower the minimalInterval on the
upload page via heartbeat_settings filter to allow sub-15s ticks.
Copilot AI review requested due to automatic review settings February 27, 2026 19:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

projects/packages/videopress/src/class-attachment-handler.php:358

  • heartbeat_received() loops over all client-provided IDs and calls get_post_meta() without any bound on the number of IDs. To avoid abuse/perf issues, consider hard-limiting the maximum IDs processed per tick (and skipping non-positive/invalid IDs) before querying meta.
		foreach ( $data['videopress_processing_ids'] as $id ) {
			$id     = (int) $id;
			$status = get_post_meta( $id, 'videopress_status', true );
			if ( $status ) {
				$statuses[ $id ] = $status;

projects/packages/videopress/src/class-attachment-handler.php:336

  • heartbeat_settings() unconditionally overwrites minimalInterval to 10. If another component already lowered this value, this will actually raise it and could break expected behavior. Consider only lowering it (e.g., take the min of the existing value and 10) rather than always overriding.
	public static function heartbeat_settings( $settings ) {
		$settings['minimalInterval'] = 10;

		return $settings;

projects/packages/videopress/src/js/media-library-poll.js:39

  • The heartbeat-send handler always forces the Heartbeat interval back to standard when there are no processing videos. This can override other features/plugins that intentionally set a different interval on the page. Consider only switching to fast when needed (and otherwise leaving the interval untouched, or restoring only if this script previously changed it).
	$( document ).on( 'heartbeat-send', function ( e, data ) {
		const ids = getProcessingVideoIds();
		if ( ids.length ) {
			data.videopress_processing_ids = ids;
			wp.heartbeat.interval( 'fast' );
		} else {
			wp.heartbeat.interval( 'standard' );
		}

projects/packages/videopress/src/js/media-library-poll.js:66

  • When Heartbeat reports an attachment is complete, the code triggers a get-attachment AJAX fetch but does not update the model status (or track an in-flight fetch). If the AJAX call is slow or fails, the attachment will stay marked as processing, causing repeated Heartbeat payloads and potentially repeated get-attachment requests on subsequent ticks. Consider marking the model as complete (or storing a “refreshing” flag) before/while fetching, and handling the AJAX failure path to avoid retry loops.
		$.each( data.videopress_processing_status, function ( id, status ) {
			const attachment = library.get( id );
			if ( attachment && status === 'complete' ) {
				wp.ajax
					.send( 'get-attachment', {
						data: { id: id },
					} )
					.done( function ( attrs ) {
						attachment.set( attrs );
					} );
			}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Labels

[Feature] VideoPress A feature to help you upload and insert videos on your site. [Package] VideoPress [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] Needs Review This PR is ready for review. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants