Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.75% covered (danger)
43.75%
196 / 448
18.18% covered (danger)
18.18%
2 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_REST_API_V2_Endpoint_VideoPress
43.69% covered (danger)
43.69%
194 / 444
18.18% covered (danger)
18.18%
2 / 11
795.19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 register_routes
97.22% covered (success)
97.22%
175 / 180
0.00% covered (danger)
0.00%
0 / 1
6
 videopress_get_settings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 videopress_update_settings
75.00% covered (warning)
75.00%
15 / 20
0.00% covered (danger)
0.00%
0 / 1
7.77
 videopress_video_belong_to_site
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 wpcom_poster_request
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
12
 videopress_block_update_poster
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 videopress_block_get_poster
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 videopress_upload_jwt
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
20
 videopress_playback_jwt
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
20
 videopress_block_update_meta
0.00% covered (danger)
0.00%
0 / 116
0.00% covered (danger)
0.00%
0 / 1
930
1<?php
2/**
3 * REST API endpoint for managing VideoPress metadata.
4 *
5 * @package automattic/jetpack
6 * @since-jetpack 9.3.0
7 * @since 0.1.3
8 */
9
10namespace Automattic\Jetpack\VideoPress;
11
12use Automattic\Jetpack\Connection\Client;
13use Automattic\Jetpack\Constants;
14use WP_Error;
15use WP_REST_Controller;
16use WP_REST_Request;
17use WP_REST_Response;
18use WP_REST_Server;
19
20if ( ! defined( 'ABSPATH' ) ) {
21    exit( 0 );
22}
23
24/**
25 * VideoPress wpcom api v2 endpoint
26 *
27 * @phan-constructor-used-for-side-effects
28 */
29class WPCOM_REST_API_V2_Endpoint_VideoPress extends WP_REST_Controller {
30    /**
31     * Constructor.
32     */
33    public function __construct() {
34        $this->namespace = 'wpcom/v2';
35        $this->rest_base = 'videopress';
36
37        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
38    }
39
40    /**
41     * Register the route.
42     */
43    public function register_routes() {
44        // Meta Route.
45        register_rest_route(
46            $this->namespace,
47            $this->rest_base . '/meta',
48            array(
49                'args'                => array(
50                    'id'              => array(
51                        'description' => __( 'The post id for the attachment.', 'jetpack-videopress-pkg' ),
52                        'type'        => 'integer',
53                        'required'    => true,
54                    ),
55                    'title'           => array(
56                        'description'       => __( 'The title of the video.', 'jetpack-videopress-pkg' ),
57                        'type'              => 'string',
58                        'sanitize_callback' => 'sanitize_text_field',
59                    ),
60                    'description'     => array(
61                        'description'       => __( 'The description of the video.', 'jetpack-videopress-pkg' ),
62                        'type'              => 'string',
63                        'sanitize_callback' => 'sanitize_textarea_field',
64                    ),
65                    'caption'         => array(
66                        'description'       => __( 'The caption of the video.', 'jetpack-videopress-pkg' ),
67                        'type'              => 'string',
68                        'sanitize_callback' => 'sanitize_textarea_field',
69                    ),
70                    'rating'          => array(
71                        'description'       => __( 'The video content rating. One of G, PG-13 or R-17', 'jetpack-videopress-pkg' ),
72                        'type'              => 'string',
73                        'sanitize_callback' => 'sanitize_text_field',
74                    ),
75                    'display_embed'   => array(
76                        'description' => __( 'Display the share menu in the player.', 'jetpack-videopress-pkg' ),
77                        'type'        => 'boolean',
78                    ),
79                    'allow_download'  => array(
80                        'description' => __( 'Display download option and allow viewers to download this video', 'jetpack-videopress-pkg' ),
81                        'type'        => 'boolean',
82                    ),
83                    'privacy_setting' => array(
84                        'description' => __( 'How to determine if the video should be public or private', 'jetpack-videopress-pkg' ),
85                        'type'        => 'integer',
86                        'enum'        => array(
87                            \VIDEOPRESS_PRIVACY::IS_PUBLIC,
88                            \VIDEOPRESS_PRIVACY::IS_PRIVATE,
89                            \VIDEOPRESS_PRIVACY::SITE_DEFAULT,
90                        ),
91                    ),
92                ),
93                'methods'             => WP_REST_Server::EDITABLE,
94                'callback'            => array( $this, 'videopress_block_update_meta' ),
95                'permission_callback' => function () {
96                    return Data::can_perform_action() && current_user_can( 'edit_posts' );
97                },
98            )
99        );
100
101        // Poster Route.
102        register_rest_route(
103            $this->namespace,
104            $this->rest_base . '/(?P<video_guid>[A-Za-z0-9]{8})/poster',
105            array(
106                'args' => array(
107                    'video_guid' => array(
108                        'description' => __( 'The VideoPress GUID.', 'jetpack-videopress-pkg' ), // @phan-suppress-current-line PhanPluginMixedKeyNoKey
109                        'type'        => 'string',
110                        'required'    => true,
111                    ),
112                ),
113                array(
114                    'methods'             => WP_REST_Server::READABLE,
115                    'callback'            => array( $this, 'videopress_block_get_poster' ),
116                    'permission_callback' => function () {
117                        return current_user_can( 'read' );
118                    },
119                ),
120                array(
121                    'args'                => array(
122                        'at_time'              => array(
123                            'description' => __( 'The time in the video to use as the poster frame.', 'jetpack-videopress-pkg' ),
124                            'type'        => 'integer',
125                        ),
126                        'is_millisec'          => array(
127                            'description' => __( 'Whether the time is in milliseconds or seconds.', 'jetpack-videopress-pkg' ),
128                            'type'        => 'boolean',
129                        ),
130                        'poster_attachment_id' => array(
131                            'description' => __( 'The attachment id of the poster image.', 'jetpack-videopress-pkg' ),
132                            'type'        => 'integer',
133                        ),
134                    ),
135                    'methods'             => WP_REST_Server::EDITABLE,
136                    'callback'            => array( $this, 'videopress_block_update_poster' ),
137                    'permission_callback' => function () {
138                        return Data::can_perform_action() && current_user_can( 'upload_files' );
139                    },
140                ),
141            )
142        );
143
144        // Endpoint to know if the video metadata is editable.
145        register_rest_route(
146            $this->namespace,
147            $this->rest_base . '/(?P<video_guid>[A-Za-z0-9]{8})/check-ownership/(?P<post_id>\d+)/',
148            array(
149                'args' => array(
150                    'video_guid' => array(
151                        'description' => __( 'The VideoPress GUID.', 'jetpack-videopress-pkg' ), // @phan-suppress-current-line PhanPluginMixedKeyNoKey
152                        'type'        => 'string',
153                        'required'    => true,
154                    ),
155                    'post_id'    => array(
156                        'description' => __( 'The post id for the attachment.', 'jetpack-videopress-pkg' ),
157                        'type'        => 'integer',
158                        'required'    => true,
159                    ),
160                ),
161                array(
162                    'methods'             => WP_REST_Server::READABLE,
163                    'callback'            => array( $this, 'videopress_video_belong_to_site' ),
164                    'permission_callback' => function () {
165                        return Data::can_perform_action() && current_user_can( 'upload_files' );
166                    },
167                ),
168            )
169        );
170
171        // Token Route.
172        register_rest_route(
173            $this->namespace,
174            $this->rest_base . '/upload-jwt',
175            array(
176                'methods'             => \WP_REST_Server::EDITABLE,
177                'callback'            => array( $this, 'videopress_upload_jwt' ),
178                'permission_callback' => function () {
179                    return Data::can_perform_action() && current_user_can( 'upload_files' );
180                },
181            )
182        );
183
184        // Playback Token Route.
185        register_rest_route(
186            $this->namespace,
187            $this->rest_base . '/playback-jwt/(?P<video_guid>[A-Za-z0-9]{8})',
188            array(
189                'args'                => array(
190                    'video_guid' => array(
191                        'description' => __( 'The VideoPress GUID.', 'jetpack-videopress-pkg' ),
192                        'type'        => 'string',
193                        'required'    => true,
194                    ),
195                ),
196                'methods'             => \WP_REST_Server::EDITABLE,
197                'callback'            => array( $this, 'videopress_playback_jwt' ),
198                'permission_callback' => function () {
199                    return current_user_can( 'read' );
200                },
201            )
202        );
203
204        // Settings Routes. Primarily for WordPress.com Simple, where the
205        // videopress/v1 namespace never reaches the REST dispatcher; the
206        // routes also register self-hosted as a harmless duplicate of
207        // videopress/v1/settings (the callbacks are host-safe).
208        register_rest_route(
209            $this->namespace,
210            $this->rest_base . '/settings',
211            array(
212                array(
213                    'methods'             => WP_REST_Server::READABLE,
214                    'callback'            => array( $this, 'videopress_get_settings' ),
215                    'permission_callback' => function () {
216                        return current_user_can( 'manage_options' );
217                    },
218                ),
219                array(
220                    'methods'             => WP_REST_Server::EDITABLE,
221                    'callback'            => array( $this, 'videopress_update_settings' ),
222                    'permission_callback' => function () {
223                        return Data::can_perform_action() && current_user_can( 'manage_options' );
224                    },
225                    'args'                => array(
226                        'videopress_videos_private_for_site' => array(
227                            'description' => __( 'If the VideoPress videos should be private by default', 'jetpack-videopress-pkg' ),
228                            'type'        => 'boolean',
229                        ),
230                        'videopress_auto_subtitles_disabled' => array(
231                            'description' => __( 'If auto-generated subtitles should be skipped for new videos', 'jetpack-videopress-pkg' ),
232                            'type'        => 'boolean',
233                        ),
234                    ),
235                ),
236            )
237        );
238    }
239
240    /**
241     * Returns the VideoPress site settings.
242     *
243     * `Data::get_videopress_settings()` is already IS_WPCOM-aware (site
244     * privacy / site type resolution), so the same callback serves every
245     * host.
246     *
247     * @return WP_REST_Response The response object.
248     */
249    public function videopress_get_settings() {
250        return rest_ensure_response( Data::get_videopress_settings() );
251    }
252
253    /**
254     * Updates the VideoPress site settings.
255     *
256     * Mirrors `VideoPress_Rest_Api_V1_Settings::update_settings()`, except
257     * on WPCOM only `videopress_auto_subtitles_disabled` is honored:
258     * `videopress_private_enabled_for_site` is a dead option on Simple,
259     * where the site-default privacy derives from the site's own privacy
260     * setting. When a caller supplies that param on WPCOM it is not
261     * persisted, and the response reports it under `ignored` so consumers
262     * are not told a write succeeded when it was silently discarded.
263     *
264     * @param WP_REST_Request $request The request object.
265     * @return WP_REST_Response The response object.
266     */
267    public function videopress_update_settings( $request ) {
268        $private_for_site        = $request->get_param( 'videopress_videos_private_for_site' );
269        $auto_subtitles_disabled = $request->get_param( 'videopress_auto_subtitles_disabled' );
270
271        $ignored = array();
272
273        // On WordPress.com Simple the site-default privacy derives from the
274        // site's own privacy setting, so `videopress_private_enabled_for_site`
275        // is a dead option. Drop the param rather than pretend to persist it,
276        // and surface it as ignored so the response stays truthful.
277        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
278            if ( null !== $private_for_site ) {
279                $ignored[] = 'videopress_videos_private_for_site';
280            }
281            $private_for_site = null;
282        }
283
284        if ( null !== $private_for_site ) {
285            update_option( 'videopress_private_enabled_for_site', $private_for_site );
286        }
287
288        if ( null !== $auto_subtitles_disabled ) {
289            update_option( 'videopress_auto_subtitles_disabled', $auto_subtitles_disabled );
290        }
291
292        $response = array(
293            'code'    => 'success',
294            'message' => __( 'VideoPress settings updated successfully.', 'jetpack-videopress-pkg' ),
295            'data'    => 200,
296        );
297
298        if ( ! empty( $ignored ) ) {
299            $response['ignored'] = $ignored;
300            $response['message'] = __( 'VideoPress settings updated. Some settings are not configurable on this site and were ignored.', 'jetpack-videopress-pkg' );
301        }
302
303        return rest_ensure_response( $response );
304    }
305
306    /**
307     * Check whether the video belongs to the current site,
308     * considering the given post_id and the video_guid.
309     *
310     * @param WP_REST_Request $request The request object.
311     * @return WP_REST_Response True if the video belongs to the current site, false otherwise.
312     */
313    public function videopress_video_belong_to_site( $request ) {
314        $post_id    = $request->get_param( 'post_id' );
315        $video_guid = $request->get_param( 'video_guid' );
316
317        if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
318            $found_guid = get_post_meta( $post_id, 'videopress_guid', true );
319        } else {
320            $blog_id    = get_current_blog_id();
321            $info       = video_get_info_by_blogpostid( $blog_id, $post_id );
322            $found_guid = $info ? $info->guid : '';
323        }
324
325        if ( ! $found_guid ) {
326            return rest_ensure_response( array( 'video-belong-to-site' => false ) );
327        }
328
329        return rest_ensure_response( array( 'video-belong-to-site' => $found_guid === $video_guid ) );
330    }
331
332    /**
333     * Hit WPCOM poster endpoint.
334     *
335     * @param string $video_guid  The VideoPress GUID.
336     * @param array  $args        Request args.
337     * @param array  $body        Request body.
338     * @param string $query       Request query.
339     * @return WP_REST_Response|WP_Error
340     */
341    public function wpcom_poster_request( $video_guid, $args, $body = null, $query = '' ) {
342        $query    = $query !== '' ? '?' . $query : '';
343        $endpoint = 'videos/' . $video_guid . '/poster' . $query;
344
345        $url = sprintf(
346            '%s/%s/v%s/%s',
347            Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
348            'rest',
349            '1.1',
350            $endpoint
351        );
352
353        $request_args = array_merge( $args, array( 'body' => $body ) );
354
355        // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
356        $result = Client::_wp_remote_request( $url, $request_args );
357
358        if ( is_wp_error( $result ) ) {
359            return rest_ensure_response( $result );
360        }
361
362        $response = $result['http_response'];
363
364        $status = $response->get_status();
365
366        $data = array(
367            'code' => $status,
368            'data' => json_decode( $response->get_data(), true ),
369        );
370
371        return rest_ensure_response(
372            new WP_REST_Response( $data, $status )
373        );
374    }
375
376    /**
377     * Update the a poster image via the WPCOM REST API.
378     *
379     * @param WP_REST_Request $request The request object.
380     * @return WP_REST_Response|WP_Error
381     */
382    public function videopress_block_update_poster( $request ) {
383        try {
384            $blog_id     = VideoPressToken::blog_id();
385            $token       = VideoPressToken::videopress_onetime_upload_token();
386            $video_guid  = $request->get_param( 'video_guid' );
387            $json_params = $request->get_json_params();
388
389            $args = array(
390                'method'  => 'POST',
391                'headers' => array(
392                    'content-type'  => 'application/json',
393                    'Authorization' => 'X_UPLOAD_TOKEN token="' . $token . '" blog_id="' . $blog_id . '"',
394                ),
395            );
396
397            return $this->wpcom_poster_request(
398                $video_guid,
399                $args,
400                wp_json_encode( $json_params, JSON_UNESCAPED_SLASHES )
401            );
402        } catch ( \Exception $e ) {
403            return rest_ensure_response( new WP_Error( 'videopress_block_update_poster_error', $e->getMessage() ) );
404        }
405    }
406
407    /**
408     * Retrieves a poster image via the WPCOM REST API.
409     *
410     * @param WP_REST_Request $request the request object.
411     * @return object|WP_Error Success object or WP_Error with error details.
412     */
413    public function videopress_block_get_poster( $request ) {
414        $video_guid = $request->get_param( 'video_guid' );
415        $jwt        = VideoPressToken::videopress_playback_jwt( $video_guid );
416
417        $args = array(
418            'method' => 'GET',
419        );
420
421        return $this->wpcom_poster_request(
422            $video_guid,
423            $args,
424            null,
425            'metadata_token=' . $jwt
426        );
427    }
428
429    /**
430     * Endpoint for getting the VideoPress Upload JWT
431     *
432     * @return WP_Rest_Response - The response object.
433     */
434    public static function videopress_upload_jwt() {
435        $has_connected_owner = Data::has_connected_owner();
436        if ( ! $has_connected_owner ) {
437            return rest_ensure_response(
438                new WP_Error(
439                    'owner_not_connected',
440                    'User not connected.',
441                    array(
442                        'code'        => 503,
443                        'connect_url' => Admin_UI::get_admin_page_url(),
444                    )
445                )
446            );
447        }
448
449        $blog_id = Data::get_blog_id();
450        if ( ! $blog_id ) {
451            return rest_ensure_response(
452                new WP_Error( 'site_not_registered', 'Site not registered.', 503 )
453            );
454        }
455
456        try {
457            $token  = VideoPressToken::videopress_upload_jwt();
458            $status = 200;
459            $data   = array(
460                'upload_token'   => $token,
461                'upload_url'     => videopress_make_resumable_upload_path( $blog_id ),
462                'upload_blog_id' => $blog_id,
463            );
464        } catch ( \Exception $e ) {
465            $status = 500;
466            $data   = array(
467                'error' => $e->getMessage(),
468            );
469
470        }
471
472        return rest_ensure_response(
473            new WP_REST_Response( $data, $status )
474        );
475    }
476
477    /**
478     * Endpoint for generating a VideoPress Playback JWT
479     *
480     * @param WP_REST_Request $request the request object.
481     * @return WP_Rest_Response - The response object.
482     */
483    public static function videopress_playback_jwt( $request ) {
484        $has_connected_owner = Data::has_connected_owner();
485        if ( ! $has_connected_owner ) {
486            return rest_ensure_response(
487                new WP_Error(
488                    'owner_not_connected',
489                    'User not connected.',
490                    array(
491                        'code'        => 503,
492                        'connect_url' => Admin_UI::get_admin_page_url(),
493                    )
494                )
495            );
496        }
497
498        $blog_id = Data::get_blog_id();
499        if ( ! $blog_id ) {
500            return rest_ensure_response(
501                new WP_Error( 'site_not_registered', 'Site not registered.', 503 )
502            );
503        }
504
505        try {
506            $video_guid = $request->get_param( 'video_guid' );
507            $token      = VideoPressToken::videopress_playback_jwt( $video_guid );
508            $status     = 200;
509            $data       = array(
510                'playback_token' => $token,
511            );
512        } catch ( \Exception $e ) {
513            $status = 500;
514            $data   = array(
515                'error' => $e->getMessage(),
516            );
517
518        }
519
520        return rest_ensure_response(
521            new WP_REST_Response( $data, $status )
522        );
523    }
524
525    /**
526     * Updates attachment meta and video metadata via the WPCOM REST API.
527     *
528     * @param WP_REST_Request $request the request object.
529     * @return object|WP_Error Success object or WP_Error with error details.
530     */
531    public function videopress_block_update_meta( $request ) {
532        $json_params = $request->get_json_params();
533        $post_id     = $json_params['id'];
534
535        if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
536            $guid = get_post_meta( $post_id, 'videopress_guid', true );
537        } else {
538            $blog_id = get_current_blog_id();
539            $info    = video_get_info_by_blogpostid( $blog_id, $post_id );
540            $guid    = $info ? $info->guid : '';
541        }
542
543        if ( ! $guid ) {
544            return rest_ensure_response(
545                new WP_Error(
546                    'error',
547                    __( 'This attachment cannot be updated yet.', 'jetpack-videopress-pkg' )
548                )
549            );
550        }
551
552        $video_request_params = $json_params;
553        unset( $video_request_params['id'] );
554        $video_request_params['guid'] = $guid;
555
556        $endpoint = 'videos';
557        $args     = array(
558            'method'  => 'POST',
559            'headers' => array( 'content-type' => 'application/json' ),
560        );
561
562        $result = Client::wpcom_json_api_request_as_blog(
563            $endpoint,
564            '2',
565            $args,
566            wp_json_encode( $video_request_params, JSON_UNESCAPED_SLASHES ),
567            'wpcom'
568        );
569
570        if ( is_wp_error( $result ) ) {
571            return rest_ensure_response( $result );
572        }
573
574        $response_body = json_decode( wp_remote_retrieve_body( $result ) );
575        if ( is_bool( $response_body ) && $response_body ) {
576            /*
577             * Title, description and caption of the video are not stored as metadata on the attachment,
578             * but as post_content, post_title and post_excerpt on the attachment's post object.
579             * We need to update those fields here, too.
580             */
581            $post_title = null;
582            if ( isset( $json_params['title'] ) ) {
583                $post_title = sanitize_text_field( $json_params['title'] );
584                wp_update_post(
585                    array(
586                        'ID'         => $post_id,
587                        'post_title' => $post_title,
588                    )
589                );
590            }
591
592            $post_content = null;
593            if ( isset( $json_params['description'] ) ) {
594                $post_content = sanitize_textarea_field( $json_params['description'] );
595                wp_update_post(
596                    array(
597                        'ID'           => $post_id,
598                        'post_content' => $post_content,
599                    )
600                );
601            }
602
603            $post_excerpt = null;
604            if ( isset( $json_params['caption'] ) ) {
605                $post_excerpt = sanitize_textarea_field( $json_params['caption'] );
606                wp_update_post(
607                    array(
608                        'ID'           => $post_id,
609                        'post_excerpt' => $post_excerpt,
610                    )
611                );
612            }
613
614            // VideoPress data is stored in attachment meta for Jetpack sites, but not on wpcom.
615            if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
616                $meta               = wp_get_attachment_metadata( $post_id );
617                $should_update_meta = false;
618
619                if ( ! $meta ) {
620                    return rest_ensure_response(
621                        new WP_Error(
622                            'error',
623                            __( 'Attachment meta was not found.', 'jetpack-videopress-pkg' )
624                        )
625                    );
626                }
627
628                if ( isset( $json_params['display_embed'] ) && isset( $meta['videopress']['display_embed'] ) ) {
629                    $meta['videopress']['display_embed'] = $json_params['display_embed'];
630                    $should_update_meta                  = true;
631                }
632
633                if ( isset( $json_params['rating'] ) && isset( $meta['videopress']['rating'] ) && videopress_is_valid_video_rating( $json_params['rating'] ) ) {
634                    $meta['videopress']['rating'] = $json_params['rating'];
635                    $should_update_meta           = true;
636
637                    /** Set a new meta field so we can filter using it directly */
638                    update_post_meta( $post_id, 'videopress_rating', $json_params['rating'] );
639                }
640
641                if ( isset( $json_params['title'] ) ) {
642                    $meta['videopress']['title'] = $post_title;
643                    $should_update_meta          = true;
644                }
645
646                if ( isset( $json_params['description'] ) ) {
647                    $meta['videopress']['description'] = $post_content;
648                    $should_update_meta                = true;
649                }
650
651                if ( isset( $json_params['caption'] ) ) {
652                    $meta['videopress']['caption'] = $post_excerpt;
653                    $should_update_meta            = true;
654                }
655
656                if ( isset( $json_params['poster'] ) ) {
657                    $meta['videopress']['poster'] = $json_params['poster'];
658                    $should_update_meta           = true;
659                }
660
661                if ( isset( $json_params['allow_download'] ) ) {
662                    $allow_download = (bool) $json_params['allow_download'];
663                    if ( ! isset( $meta['videopress']['allow_download'] ) || $meta['videopress']['allow_download'] !== $allow_download ) {
664                        $meta['videopress']['allow_download'] = $allow_download;
665                        $should_update_meta                   = true;
666                    }
667                }
668
669                if ( isset( $json_params['privacy_setting'] ) ) {
670                    $privacy_setting = $json_params['privacy_setting'];
671                    if ( ! isset( $meta['videopress']['privacy_setting'] ) || $meta['videopress']['privacy_setting'] !== $privacy_setting ) {
672                        $meta['videopress']['privacy_setting'] = $privacy_setting;
673                        $should_update_meta                    = true;
674
675                        /** Set a new meta field so we can filter using it directly */
676                        update_post_meta( $post_id, 'videopress_privacy_setting', $privacy_setting );
677                    }
678                }
679
680                if ( $should_update_meta ) {
681                    wp_update_attachment_metadata( $post_id, $meta );
682                }
683            }
684
685            return rest_ensure_response(
686                array(
687                    'code'    => 'success',
688                    'message' => __( 'Video meta updated successfully.', 'jetpack-videopress-pkg' ),
689                    'data'    => 200,
690                )
691            );
692        } else {
693            return rest_ensure_response(
694                new WP_Error(
695                    $response_body->code,
696                    $response_body->message,
697                    $response_body->data
698                )
699            );
700        }
701    }
702}
703
704if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
705    wpcom_rest_api_v2_load_plugin( 'Automattic\Jetpack\VideoPress\WPCOM_REST_API_V2_Endpoint_VideoPress' );
706}