Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Significance: minor
Type: added

Add an optional trim and cut editor with preview, undo, original video restoration, and a choice to update or save a new video. Keep the editor available during processing and resume pending copies when returning to the page. Reduce background status checks, refresh delayed timeline thumbnails, and allow retrying failed edits without creating another video.
Add an optional trim and cut editor with preview, undo, and original video restoration. Keep the editor available during processing, reduce background status checks, refresh delayed timeline thumbnails, and allow retrying failed edits.
Original file line number Diff line number Diff line change
Expand Up @@ -135,46 +135,6 @@ public function register_routes() {
'permission_callback' => array( $this, 'permissions_check' ),
)
);

$copy_args = array_merge(
$guid_arg,
array(
'request_id' => array(
'type' => 'string',
'pattern' => '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$',
'required' => true,
),
)
);
register_rest_route(
'wpcom/v2',
'videopress/(?P<guid>[A-Za-z0-9]{8})/edits/copy',
array(
'args' => array_merge(
$copy_args,
$edit_args,
array(
'title' => array(
'type' => 'string',
'maxLength' => 1000,
),
)
),
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'copy_edits' ),
'permission_callback' => array( $this, 'permissions_check' ),
)
);
register_rest_route(
'wpcom/v2',
'videopress/(?P<guid>[A-Za-z0-9]{8})/edits/copy/(?P<request_id>[a-f0-9-]{36})',
array(
'args' => $copy_args,
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_copy' ),
'permission_callback' => array( $this, 'permissions_check' ),
)
);
}

/**
Expand Down Expand Up @@ -230,34 +190,6 @@ public function retry_edits( $request ) {
);
}

/**
* Create an independent video from original-timeline edits.
*
* @param WP_REST_Request $request The request object.
* @return WP_REST_Response|WP_Error
*/
public function copy_edits( $request ) {
$body = array(
'base_revision' => $request['base_revision'],
'operations' => $request['operations'],
'request_id' => $request['request_id'],
);
if ( isset( $request['title'] ) ) {
$body['title'] = $request['title'];
}
return $this->proxy_request( sprintf( 'videos/%s/edits/copy', $request['guid'] ), 'POST', $body, true );
}

/**
* Fetch progress for the same idempotent copy request.
*
* @param WP_REST_Request $request The request object.
* @return WP_REST_Response|WP_Error
*/
public function get_copy( $request ) {
return $this->proxy_request( sprintf( 'videos/%s/edits/copy/%s', $request['guid'], $request['request_id'] ) );
}

/**
* Restore the original using the upstream API's POST deletion convention.
*
Expand All @@ -284,10 +216,9 @@ public function get_storyboard( $request ) {
* @param string $path WordPress.com REST v1.1 path.
* @param string $method HTTP method.
* @param array|null $body JSON request data.
* @param bool $as_user Preserve the connected actor when creating a copy.
* @return WP_REST_Response|WP_Error
*/
private function proxy_request( $path, $method = 'GET', $body = null, $as_user = false ) {
private function proxy_request( $path, $method = 'GET', $body = null ) {
$args = array( 'method' => $method );
if ( null !== $body ) {
$args['headers'] = array( 'content-type' => 'application/json' );
Expand All @@ -307,8 +238,6 @@ private function proxy_request( $path, $method = 'GET', $body = null, $as_user =
$url = Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ) . '/rest/v1.1/' . $path;
// @phan-suppress-next-line PhanAccessMethodInternal -- Use the poster transport; the direct client only dispatches v2 routes.
$response = Client::_wp_remote_request( $url, $args );
} elseif ( $as_user ) {
$response = Client::wpcom_json_api_request_as_user( $path, '1.1', $args, $body, 'rest' );
} else {
$response = Client::wpcom_json_api_request_as_blog( $path, '1.1', $args, $body, 'rest' );
}
Expand Down
89 changes: 0 additions & 89 deletions projects/packages/videopress/src/class-xmlrpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Automattic\Jetpack\VideoPress;

use WP_Error;
use WP_User;

/**
Expand Down Expand Up @@ -68,51 +67,12 @@ public function xmlrpc_methods( $methods, $core_methods, $user ) {
}

$methods['jetpack.createMediaItem'] = array( $this, 'create_media_item' );
$methods['jetpack.createVideoPressCopy'] = array( $this, 'create_videopress_copy' );
$methods['jetpack.authorizeVideoPressCopy'] = array( $this, 'authorize_videopress_copy' );
$methods['jetpack.updateVideoPressMediaItem'] = array( $this, 'update_videopress_media_item' );
$methods['jetpack.updateVideoPressPosterImage'] = array( $this, 'update_poster_image' );

return $methods;
}

/**
* Check permissions on the real attachment before WordPress.com copies its retained master.
*
* @since $$next-version$$
* @param string $guid The source VideoPress GUID.
* @return array Permission acknowledgement or an error response.
*/
public function authorize_videopress_copy( $guid ) {
$this->authenticate_user();
if ( ! is_string( $guid ) || ! preg_match( '/^[A-Za-z0-9]{8}$/D', $guid )
|| ! $this->current_user || ! $this->current_user->exists() || ! current_user_can( 'upload_files' ) ) {
return array( 'errors' => array( 'videopress_copy_forbidden' => __( 'You cannot copy this video.', 'jetpack-videopress-pkg' ) ) );
}
$post_id = WPCOM_REST_API_V2_Endpoint_VideoPress::get_video_attachment_id( $guid );
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
return array( 'errors' => array( 'videopress_copy_forbidden' => __( 'You cannot copy this video.', 'jetpack-videopress-pkg' ) ) );
}
return array(
'authorized' => true,
'guid' => $guid,
);
}

/**
* Create one idempotent copy attachment; older clients reject this method before inserting a row.
*
* @since $$next-version$$
* @param array $media A single media item carrying its copy request identifier.
* @return array The attachment or an error response.
*/
public function create_videopress_copy( $media ) {
if ( ! is_array( $media ) || count( $media ) !== 1 || ! isset( $media[0] ) || ! is_array( $media[0] ) || ! array_key_exists( 'videopress_copy_request_id', $media[0] ) ) {
return array( 'errors' => array( 'videopress_copy_invalid_request' => __( 'Invalid video copy request.', 'jetpack-videopress-pkg' ) ) );
}
return $this->create_media_item( $media );
}

/**
* This is used by the WPCOM VideoPress uploader in order to create a media item with
* specific meta data about an uploaded file. After this, the transcoding session will
Expand All @@ -134,16 +94,6 @@ public function create_media_item( $media ) {
: sanitize_title( basename( $url ) );
$guid = $media['guid'] ?? null;

if ( array_key_exists( 'videopress_copy_request_id', $media_item ) ) {
$media_id = $this->create_copy_attachment( $title, $media_item['videopress_copy_request_id'] );
if ( is_wp_error( $media_id ) ) {
return array( 'errors' => array( $media_id->get_error_code() => $media_id->get_error_message() ) );
}
$media_item['post'] = get_post( $media_id );
$media_item['videopress_copy_request_id_ack'] = $media_item['videopress_copy_request_id'];
continue;
}

$media_id = videopress_create_new_media_item( $title, $guid );

$post_update = array();
Expand Down Expand Up @@ -173,45 +123,6 @@ public function create_media_item( $media ) {
return array( 'media' => $media );
}

/**
* Reserve a copy request before inserting its attachment so retries cannot create duplicates.
*
* @param string $title Attachment title.
* @param mixed $request_id Source GUID and copy request UUID.
* @return int|WP_Error The existing or newly created attachment ID.
*/
private function create_copy_attachment( $title, $request_id ) {
if ( ! is_string( $request_id ) || ! preg_match( '/^[A-Za-z0-9]{8}:[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/D', $request_id ) ) {
return new WP_Error( 'videopress_copy_invalid_request', __( 'Invalid video copy request identifier.', 'jetpack-videopress-pkg' ) );
}
if ( ! $this->current_user || ! $this->current_user->exists() || ! current_user_can( 'upload_files' ) ) {
return new WP_Error( 'videopress_copy_forbidden', __( 'You cannot create a video copy.', 'jetpack-videopress-pkg' ) );
}

$option = 'videopress_copy_attachment_' . hash( 'sha256', $request_id );
if ( ! add_option( $option, 0, '', false ) ) {
$attachment_id = (int) get_option( $option, 0 );
if ( ! $attachment_id ) {
return new WP_Error( 'videopress_copy_attachment_pending', __( 'The video copy attachment is still being created.', 'jetpack-videopress-pkg' ) );
}
if ( 'attachment' !== get_post_type( $attachment_id ) || get_post_meta( $attachment_id, '_videopress_copy_request_id', true ) !== $request_id ) {
return new WP_Error( 'videopress_copy_attachment_unavailable', __( 'The video copy attachment is unavailable.', 'jetpack-videopress-pkg' ) );
}
return $attachment_id;
}

// A pending reservation never expires: a timed-out insert may already have created the attachment.
$attachment_id = videopress_create_new_media_item( $title );
if ( is_wp_error( $attachment_id ) || ! $attachment_id ) {
return new WP_Error( 'videopress_copy_attachment_failed', __( 'The video copy attachment could not be created.', 'jetpack-videopress-pkg' ) );
}
wp_update_attachment_metadata( $attachment_id, array( 'original' => array( 'url' => '' ) ) );
if ( ! add_post_meta( $attachment_id, '_videopress_copy_request_id', $request_id, true ) || ! update_option( $option, $attachment_id, false ) ) {
return new WP_Error( 'videopress_copy_attachment_failed', __( 'The video copy attachment could not be recorded.', 'jetpack-videopress-pkg' ) );
}
return $attachment_id;
}

/**
* Update VideoPress metadata for a media item.
*
Expand Down

This file was deleted.

This file was deleted.

Loading
Loading