PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-videopress / src / utility-functions.php

utility-functions.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at jetpack_vendor/automattic/jetpack-videopress/src/utility-functions.php

806 lines 22.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 use Automattic\Jetpack\Connection\Client;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit( 0 );
7 }
8
9 defined( 'VIDEOPRESS_MIN_WIDTH' ) || define( 'VIDEOPRESS_MIN_WIDTH', 60 );
10 defined( 'VIDEOPRESS_DEFAULT_WIDTH' ) || define( 'VIDEOPRESS_DEFAULT_WIDTH', 640 );
11
12 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
13
14 /**
15 * VideoPress Privacy constants.
16 */
17 abstract class VIDEOPRESS_PRIVACY {
18 const IS_PUBLIC = 0;
19 const IS_PRIVATE = 1;
20 const SITE_DEFAULT = 2;
21 }
22
23 /**
24 * Validate user-supplied guid values against expected inputs
25 *
26 * @since 1.1
27 * @param string $guid video identifier.
28 * @return bool true if passes validation test
29 */
30 function videopress_is_valid_guid( $guid ) {
31 if ( ! empty( $guid ) && is_string( $guid ) && strlen( $guid ) === 8 && ctype_alnum( $guid ) ) {
32 return true;
33 }
34 return false;
35 }
36
37 /**
38 * Validates user-supplied video preload setting.
39 *
40 * @param mixed $value the preload value to validate.
41 * @return bool
42 */
43 function videopress_is_valid_preload( $value ) {
44 return in_array( strtolower( $value ), array( 'auto', 'metadata', 'none' ), true );
45 }
46
47 /**
48 * Get details about a specific video by GUID:
49 *
50 * @param string $guid Video GUID.
51 * @return object
52 */
53 function videopress_get_video_details( $guid ) {
54 if ( ! videopress_is_valid_guid( $guid ) ) {
55 return new WP_Error( 'bad-guid-format', __( 'Invalid Video GUID!', 'jetpack-videopress-pkg' ) );
56 }
57
58 $version = '1.1';
59 $endpoint = sprintf( '/videos/%1$s', $guid );
60 $query_url = sprintf(
61 'https://public-api.wordpress.com/rest/v%1$s%2$s',
62 $version,
63 $endpoint
64 );
65
66 // Look for data in our transient. If nothing, let's make a new query.
67 $data_from_cache = get_transient( 'jetpack_videopress_' . $guid );
68 if ( false === $data_from_cache ) {
69 $response = wp_remote_get( esc_url_raw( $query_url ) );
70 $data = json_decode( wp_remote_retrieve_body( $response ) );
71
72 // Cache the response for an hour.
73 set_transient( 'jetpack_videopress_' . $guid, $data, HOUR_IN_SECONDS );
74 } else {
75 $data = $data_from_cache;
76 }
77
78 /**
79 * Allow functions to modify fetched video details.
80 *
81 * This filter allows third-party code to modify the return data
82 * about a given video. It may involve swapping some data out or
83 * adding new parameters.
84 *
85 * @since 4.0.0
86 *
87 * @param object $data The data returned by the WPCOM API. See: https://developer.wordpress.com/docs/api/1.1/get/videos/%24guid/
88 * @param string $guid The GUID of the VideoPress video in question.
89 */
90 return apply_filters( 'videopress_get_video_details', $data, $guid );
91 }
92
93 /**
94 * Similar to `media_sideload_image` -- but returns an ID.
95 *
96 * @param string $url Image URL.
97 * @param int $attachment_id Post ID.
98 *
99 * @return int|mixed|object|WP_Error
100 */
101 function videopress_download_poster_image( $url, $attachment_id ) {
102 // Set variables for storage, fix file filename for query strings.
103 preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $url, $matches );
104 if ( ! $matches ) {
105 return new WP_Error( 'image_sideload_failed', __( 'Invalid image URL', 'jetpack-videopress-pkg' ) );
106 }
107
108 $file_array = array();
109 $file_array['name'] = basename( $matches[0] );
110 $file_array['tmp_name'] = download_url( $url );
111
112 // If error storing temporarily, return the error.
113 if ( is_wp_error( $file_array['tmp_name'] ) ) {
114 return $file_array['tmp_name'];
115 }
116
117 // Do the validation and storage stuff.
118 $thumbnail_id = media_handle_sideload( $file_array, $attachment_id, null );
119
120 // Flag it as poster image, so we can exclude it from display.
121 update_post_meta( $thumbnail_id, 'videopress_poster_image', 1 );
122
123 return $thumbnail_id;
124 }
125
126 /**
127 * Creates a local media library item of a remote VideoPress video.
128 *
129 * @param string $guid Video GUID.
130 * @param int $parent_id Parent post ID.
131 *
132 * @return int|object
133 */
134 function create_local_media_library_for_videopress_guid( $guid, $parent_id = 0 ) {
135 $vp_data = videopress_get_video_details( $guid );
136 if ( ! $vp_data || is_wp_error( $vp_data ) ) {
137 return $vp_data;
138 }
139
140 $args = array(
141 'post_date' => $vp_data->upload_date,
142 'post_title' => wp_kses( $vp_data->title, array() ),
143 'post_content' => wp_kses( $vp_data->description, array() ),
144 'post_mime_type' => 'video/videopress',
145 'guid' => sprintf( 'https://videopress.com/v/%s', $guid ),
146 );
147
148 $attachment_id = wp_insert_attachment( $args, null, $parent_id );
149
150 if ( ! is_wp_error( $attachment_id ) ) {
151 update_post_meta( $attachment_id, 'videopress_guid', $guid );
152 wp_update_attachment_metadata(
153 $attachment_id,
154 array(
155 'width' => $vp_data->width,
156 'height' => $vp_data->height,
157 )
158 );
159
160 $thumbnail_id = videopress_download_poster_image( $vp_data->poster, $attachment_id );
161 update_post_meta( $attachment_id, '_thumbnail_id', $thumbnail_id );
162 }
163
164 return $attachment_id;
165 }
166
167 /**
168 * Helper that will look for VideoPress media items that are more than 30 minutes old,
169 * that have not had anything attached to them by a wpcom upload and deletes the ghost
170 * attachment.
171 *
172 * These happen primarily because of failed upload attempts.
173 *
174 * @return int The number of items that were cleaned up.
175 */
176 function videopress_cleanup_media_library() {
177 // phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable -- Function is disabled currently.
178 // Disable this job for now.
179 return 0;
180 $query_args = array(
181 'post_type' => 'attachment',
182 'post_status' => 'inherit',
183 'post_mime_type' => 'video/videopress',
184 'meta_query' => array(
185 array(
186 'key' => 'videopress_status',
187 'value' => 'new',
188 ),
189 ),
190 );
191
192 $query = new WP_Query( $query_args );
193
194 $cleaned = 0;
195
196 $now = current_time( 'timestamp' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Probably should refactor, but this function is disabled.
197
198 if ( $query->have_posts() ) {
199 foreach ( $query->posts as $post ) {
200 $post_time = strtotime( $post->post_date_gmt );
201
202 // If the post is older than 30 minutes, it is safe to delete it.
203 if ( $now - $post_time > MINUTE_IN_SECONDS * 30 ) {
204 // Force delete the attachment, because we don't want it appearing in the trash.
205 wp_delete_attachment( $post->ID, true );
206
207 ++$cleaned;
208 }
209 }
210 }
211
212 return $cleaned;
213 // phpcs:enable Squiz.PHP.NonExecutableCode.Unreachable
214 }
215
216 /**
217 * Return an absolute URI for a given filename and guid on the CDN.
218 * No check is performed to ensure the guid exists or the file is present. Simple centralized string builder.
219 *
220 * @param string $guid VideoPress identifier.
221 * @param string $filename name of file associated with the guid (video file name or thumbnail file name).
222 *
223 * @return string Absolute URL of VideoPress file for the given guid.
224 */
225 function videopress_cdn_file_url( $guid, $filename ) {
226 return "https://videos.files.wordpress.com/{$guid}/{$filename}";
227 }
228
229 /**
230 * Get an array of the transcoding status for the given video post.
231 *
232 * @since 4.4
233 * @param int $post_id Post ID.
234 * @return array|bool Returns an array of statuses if this is a VideoPress post, otherwise it returns false.
235 */
236 function videopress_get_transcoding_status( $post_id ) {
237 $meta = wp_get_attachment_metadata( $post_id );
238
239 // If this has not been processed by videopress, we can skip the rest.
240 if ( ! $meta || ! isset( $meta['file_statuses'] ) ) {
241 return false;
242 }
243
244 $info = (object) $meta['file_statuses'];
245
246 $status = array(
247 'std_mp4' => $info->mp4 ?? null,
248 'std_ogg' => $info->ogg ?? null,
249 'dvd_mp4' => $info->dvd ?? null,
250 'hd_mp4' => $info->hd ?? null,
251 );
252
253 return $status;
254 }
255
256 /**
257 * Get the direct url to the video.
258 *
259 * @since 4.4
260 * @param string $guid VideoPress GUID.
261 * @return string
262 */
263 function videopress_build_url( $guid ) {
264
265 // No guid, no videopress url.
266 if ( ! $guid ) {
267 return '';
268 }
269
270 return 'https://videopress.com/v/' . $guid;
271 }
272
273 /**
274 * Create an empty videopress media item that will be filled out later by an xmlrpc
275 * callback from the VideoPress servers.
276 *
277 * @since 4.4
278 * @param string $title The post_title.
279 * @param string $guid The VideoPress guid.
280 * @return int|WP_Error
281 */
282 function videopress_create_new_media_item( $title, $guid = null ) {
283 $post = array(
284 'post_type' => 'attachment',
285 'post_mime_type' => 'video/videopress',
286 'post_title' => $title,
287 'post_content' => '',
288 'guid' => videopress_build_url( $guid ),
289 );
290
291 $media_id = wp_insert_post( $post );
292
293 add_post_meta( $media_id, 'videopress_status', 'initiated' );
294
295 add_post_meta( $media_id, 'videopress_guid', $guid );
296
297 return $media_id;
298 }
299
300 /**
301 * Merge VideoPress file status metadata.
302 *
303 * @param array $current_status The current status of the video.
304 * @param array $new_meta The new meta data to merge with the current status.
305 * @return array
306 */
307 function videopress_merge_file_status( $current_status, $new_meta ) {
308 $new_statuses = array();
309
310 if ( isset( $new_meta['videopress']['files_status']['hd'] ) ) {
311 $new_statuses['hd'] = $new_meta['videopress']['files_status']['hd'];
312 }
313
314 if ( isset( $new_meta['videopress']['files_status']['dvd'] ) ) {
315 $new_statuses['dvd'] = $new_meta['videopress']['files_status']['dvd'];
316 }
317
318 if ( isset( $new_meta['videopress']['files_status']['std']['mp4'] ) ) {
319 $new_statuses['mp4'] = $new_meta['videopress']['files_status']['std']['mp4'];
320 }
321
322 if ( isset( $new_meta['videopress']['files_status']['std']['ogg'] ) ) {
323 $new_statuses['ogg'] = $new_meta['videopress']['files_status']['std']['ogg'];
324 }
325
326 foreach ( $new_statuses as $format => $status ) {
327 if ( ! isset( $current_status[ $format ] ) ) {
328 $current_status[ $format ] = $status;
329 continue;
330 }
331
332 if ( $current_status[ $format ] !== 'DONE' ) {
333 $current_status[ $format ] = $status;
334 }
335 }
336
337 return $current_status;
338 }
339
340 /**
341 * Check to see if a video has completed processing.
342 *
343 * @since 4.4
344 * @param int $post_id Post ID.
345 * @return bool
346 */
347 function videopress_is_finished_processing( $post_id ) {
348 $post = get_post( $post_id );
349
350 if ( is_wp_error( $post ) ) {
351 return false;
352 }
353
354 $meta = wp_get_attachment_metadata( $post->ID );
355 if ( ! isset( $meta['videopress']['finished'] ) ) {
356 return false;
357 }
358
359 return $meta['videopress']['finished'];
360 }
361
362 /**
363 * Update the meta information status for the given video post.
364 *
365 * @since 4.4
366 * @param int $post_id Post ID.
367 * @return bool
368 */
369 function videopress_update_meta_data( $post_id ) {
370
371 $meta = wp_get_attachment_metadata( $post_id );
372
373 // If this has not been processed by VideoPress, we can skip the rest.
374 if ( ! $meta || ! isset( $meta['videopress'] ) ) {
375 return false;
376 }
377
378 $info = (object) $meta['videopress'];
379
380 // Without a guid there is no video to query for.
381 if ( ! isset( $info->guid ) ) {
382 return false;
383 }
384
385 $result = Client::wpcom_json_api_request_as_blog( 'videos/' . $info->guid );
386
387 if ( is_wp_error( $result ) ) {
388 return false;
389 }
390
391 $response = json_decode( $result['body'], true );
392
393 // Update the attachment metadata.
394 $meta['videopress'] = $response;
395
396 wp_update_attachment_metadata( $post_id, $meta );
397
398 return true;
399 }
400
401 /**
402 * Check to see if this is a VideoPress post that hasn't had a guid set yet.
403 *
404 * @param int $post_id Post ID.
405 * @return bool
406 */
407 function videopress_is_attachment_without_guid( $post_id ) {
408 $post = get_post( $post_id );
409
410 if ( is_wp_error( $post ) ) {
411 return false;
412 }
413
414 if ( $post->post_mime_type !== 'video/videopress' ) {
415 return false;
416 }
417
418 $videopress_guid = get_post_meta( $post_id, 'videopress_guid', true );
419
420 if ( $videopress_guid ) {
421 return false;
422 }
423
424 return true;
425 }
426
427 /**
428 * Check to see if this is a VideoPress attachment.
429 *
430 * @param int $post_id Post ID.
431 * @return bool
432 */
433 function is_videopress_attachment( $post_id ) {
434 $post = get_post( $post_id );
435
436 if ( ! $post instanceof WP_Post ) {
437 return false;
438 }
439
440 if ( $post->post_mime_type !== 'video/videopress' ) {
441 return false;
442 }
443
444 return true;
445 }
446
447 /**
448 * Get the video update path
449 *
450 * @since 4.4
451 * @param string $guid VideoPress GUID.
452 * @return string
453 */
454 function videopress_make_video_get_path( $guid ) {
455 return sprintf(
456 '%s/rest/v%s/videos/%s',
457 JETPACK__WPCOM_JSON_API_BASE,
458 Client::WPCOM_JSON_API_VERSION,
459 $guid
460 );
461 }
462
463 /**
464 * Get the upload api path.
465 *
466 * @since 4.4
467 * @param int $blog_id The id of the blog we're uploading to.
468 * @return string
469 */
470 function videopress_make_media_upload_path( $blog_id ) {
471 return sprintf(
472 'https://public-api.wordpress.com/rest/v1.1/sites/%s/media/new?locale=%s',
473 $blog_id,
474 get_locale()
475 );
476 }
477
478 /**
479 * Get the resumable upload api path.
480 *
481 * @since 4.4
482 * @param int $blog_id The id of the blog we're uploading to.
483 * @return string
484 */
485 function videopress_make_resumable_upload_path( $blog_id ) {
486 return sprintf(
487 'https://public-api.wordpress.com/rest/v1.1/video-uploads/%s/',
488 $blog_id
489 );
490 }
491
492 /**
493 * This is a mock of the internal VideoPress method, which is meant to duplicate the functionality
494 * of the WPCOM API, so that the Jetpack REST API returns the same data with no modifications.
495 *
496 * @param int $blog_id Blog ID.
497 * @param int $post_id Post ID.
498 * @param bool $cache_bust Ignored. The real WPCOM function caches reads for 12 hours and this
499 * param forces a fresh read; the mock reads local postmeta directly.
500 * Mirrored here so IS_WPCOM call sites can pass it without the shared
501 * signature diverging from the real one.
502 * @return stdClass
503 */
504 function video_get_info_by_blogpostid( $blog_id, $post_id, $cache_bust = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- see docblock.
505 $post = get_post( $post_id );
506
507 $video_info = new stdClass();
508 $video_info->post_id = 0;
509 $video_info->description = '';
510 $video_info->title = '';
511 $video_info->caption = '';
512 $video_info->blog_id = $blog_id;
513 $video_info->guid = null;
514 $video_info->finish_date_gmt = '0000-00-00 00:00:00';
515 $video_info->rating = null;
516 $video_info->privacy_setting = VIDEOPRESS_PRIVACY::SITE_DEFAULT;
517
518 if ( ! $post ) {
519 return $video_info;
520 }
521
522 $video_info->post_id = $post_id;
523 $video_info->description = $post->post_content;
524 $video_info->title = $post->post_title;
525 $video_info->caption = $post->post_excerpt;
526
527 if ( 'video/videopress' !== $post->post_mime_type ) {
528 return $video_info;
529 }
530
531 // Since this is a VideoPress post, lt's fill out the rest of the object.
532 $video_info->guid = get_post_meta( $post_id, 'videopress_guid', true );
533 $meta = wp_get_attachment_metadata( $post_id );
534
535 if ( $meta && isset( $meta['videopress'] ) ) {
536 $videopress_meta = $meta['videopress'];
537 $video_info->rating = $videopress_meta['rating'] ?? null;
538 $video_info->allow_download = $videopress_meta['allow_download'] ?? 0;
539 $video_info->display_embed = $videopress_meta['display_embed'] ?? 0;
540 $video_info->privacy_setting = ! isset( $videopress_meta['privacy_setting'] ) ? VIDEOPRESS_PRIVACY::SITE_DEFAULT : $videopress_meta['privacy_setting'];
541
542 if ( ! empty( $videopress_meta['finished'] ) ) {
543 $video_info->finish_date_gmt = gmdate( 'Y-m-d H:i:s', (int) $videopress_meta['finished'] );
544 }
545 }
546
547 /** Make sure we are keeping some meta keys updated for filtering purposes */
548 if ( get_post_meta( $post_id, 'videopress_rating', true ) !== $video_info->rating ) {
549 update_post_meta( $post_id, 'videopress_rating', $video_info->rating );
550 }
551 if ( get_post_meta( $post_id, 'videopress_privacy_setting', true ) !== $video_info->privacy_setting ) {
552 update_post_meta( $post_id, 'videopress_privacy_setting', $video_info->privacy_setting );
553 }
554
555 return $video_info;
556 }
557
558 /**
559 * Check that a VideoPress video format has finished processing.
560 *
561 * This uses the info object, because that is what the WPCOM endpoint
562 * uses, however we don't have a complete info object in the same way
563 * WPCOM does, so we pull the meta information out of the post
564 * options instead.
565 *
566 * Note: This mimics the WPCOM function of the same name and helps the media
567 * API endpoint add all needed VideoPress data.
568 *
569 * @param stdClass $info Info object.
570 * @param string $format Video format.
571 * @return bool
572 */
573 function video_format_done( $info, $format ) {
574
575 // Avoids notice when a non-videopress item is found.
576 if ( ! is_object( $info ) ) {
577 return false;
578 }
579
580 $post_id = $info->post_id;
581
582 if ( get_post_mime_type( $post_id ) !== 'video/videopress' ) {
583 return false;
584 }
585
586 $post = get_post( $post_id );
587
588 if ( is_wp_error( $post ) ) {
589 return false;
590 }
591
592 $meta = wp_get_attachment_metadata( $post->ID );
593
594 $video_format = str_replace( array( 'fmt_', 'fmt1_' ), '', $format );
595
596 if ( 'ogg' === $video_format ) {
597 return isset( $meta['videopress']['files']['std']['ogg'] );
598 } else {
599 return isset( $meta['videopress']['files'][ $video_format ]['mp4'] );
600 }
601 }
602
603 /**
604 * Get the image URL for the given VideoPress GUID
605 *
606 * We look up by GUID, because that is what WPCOM does and this needs to be
607 * parameter compatible with that.
608 *
609 * Note: This mimics the WPCOM function of the same name and helps the media
610 * API endpoint add all needed VideoPress data.
611 *
612 * @param string $guid VideoPress GUID.
613 * @param string $format Video format.
614 * @return string|null The poster image URL, or null if it cannot be resolved.
615 */
616 function video_image_url_by_guid( $guid, $format ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
617
618 $post = videopress_get_post_by_guid( $guid );
619
620 if ( is_wp_error( $post ) || ! $post ) {
621 return null;
622 }
623
624 $meta = wp_get_attachment_metadata( $post->ID );
625
626 if ( ! is_array( $meta ) || ! isset( $meta['videopress']['poster'] ) ) {
627 return null;
628 }
629
630 $poster = apply_filters( 'jetpack_photon_url', $meta['videopress']['poster'] );
631
632 return $poster;
633 }
634
635 /**
636 * Using a GUID, find a post.
637 *
638 * @param string $guid The post guid.
639 * @return WP_Post|false The post for that guid, or false if none is found.
640 */
641 function videopress_get_post_by_guid( $guid ) {
642 $cache_key = 'get_post_by_guid_' . $guid;
643 $cache_group = 'videopress';
644 $cached_post = wp_cache_get( $cache_key, $cache_group );
645
646 if ( is_object( $cached_post ) && 'WP_Post' === get_class( $cached_post ) ) {
647 return $cached_post;
648 }
649
650 $post_id = videopress_get_post_id_by_guid( $guid );
651
652 if ( is_int( $post_id ) ) {
653 $post = get_post( $post_id );
654 wp_cache_set( $cache_key, $post, $cache_group, HOUR_IN_SECONDS );
655
656 return $post;
657 }
658
659 return false;
660 }
661
662 /**
663 * Using a GUID, find the associated post ID.
664 *
665 * @since 8.4.0
666 * @param string $guid The guid to look for the post ID of.
667 * @return int|false The post ID for that guid, or false if none is found.
668 */
669 function videopress_get_post_id_by_guid( $guid ) {
670 $cache_key = 'videopress_get_post_id_by_guid_' . $guid;
671 $cached_id = get_transient( $cache_key );
672
673 if ( is_int( $cached_id ) ) {
674 return $cached_id;
675 }
676
677 $args = array(
678 'post_type' => 'attachment',
679 'post_mime_type' => 'video/videopress',
680 'post_status' => 'inherit',
681 'no_found_rows' => true,
682 'fields' => 'ids',
683 'meta_query' => array(
684 array(
685 'key' => 'videopress_guid',
686 'value' => $guid,
687 'compare' => '=',
688 ),
689 ),
690 );
691
692 $query = new WP_Query( $args );
693
694 if ( $query->have_posts() ) {
695 $post_id = $query->next_post();
696 set_transient( $cache_key, $post_id, HOUR_IN_SECONDS );
697
698 return $post_id;
699 }
700
701 return false;
702 }
703
704 /**
705 * From the given VideoPress post_id, return back the appropriate attachment URL.
706 *
707 * When the MP4 hasn't been processed yet or this is not a VideoPress video, this will return null.
708 *
709 * @param int $post_id Post ID of the attachment.
710 * @return string|null
711 */
712 function videopress_get_attachment_url( $post_id ) {
713
714 // We only handle VideoPress attachments.
715 if ( get_post_mime_type( $post_id ) !== 'video/videopress' ) {
716 return null;
717 }
718
719 $meta = wp_get_attachment_metadata( $post_id );
720
721 // As of Jetpack 10.3 transcoded video files are reserved for the VideoPress player.
722 // All other video file requests will receive the originally uploaded file, stored on the wpcom cdn.
723 if ( ! isset( $meta['videopress']['original'] ) ) {
724 // Use the original file as the url if it isn't transcoded yet.
725 if ( isset( $meta['original'] ) ) {
726 $return = $meta['original'];
727 } else {
728 // Otherwise, there isn't much we can do.
729 return null;
730 }
731 } else {
732 $return = $meta['videopress']['original'];
733 }
734
735 // If the URL is a string, return it. Otherwise, we shouldn't to avoid errors downstream, so null.
736 return ( is_string( $return ) ) ? $return : null;
737 }
738
739 /**
740 * Converts VideoPress flash embeds into oEmbed-able URLs.
741 *
742 * Older VideoPress embed depended on Flash, which no longer work,
743 * so let us convert them to an URL that WordPress can oEmbed.
744 *
745 * Note that this file is always loaded via modules/module-extras.php and is not dependent on module status.
746 *
747 * @param string $content the content.
748 * @return string filtered content
749 */
750 function jetpack_videopress_flash_embed_filter( $content ) {
751 // This receives data from the `the_content` filter, which unfortunately sometimes has bad content passed along as a param.
752 if ( ! is_string( $content ) ) {
753 return $content;
754 }
755 $regex = '%<embed[^>]*+>(?:\s*</embed>)?%i';
756 $content = preg_replace_callback(
757 $regex,
758 function ( $matches ) {
759 $embed_code = $matches[0];
760 $url_matches = array();
761
762 // get video ID from flash URL.
763 $url_matched = preg_match( '/src="http:\/\/v.wordpress.com\/([^"]+)"/', $embed_code, $url_matches );
764
765 if ( $url_matched ) {
766 $video_id = $url_matches[1];
767 return "https://videopress.com/v/$video_id";
768 }
769 },
770 $content
771 );
772 return $content;
773 }
774
775 /**
776 * Checks if the provided rating string is a valid VideoPress video rating value.
777 *
778 * @param mixed $rating The video rating to validate.
779 * @return bool
780 */
781 function videopress_is_valid_video_rating( $rating ) {
782 return in_array( $rating, array( 'G', 'PG-13', 'R-17' ), true );
783 }
784
785 add_filter( 'the_content', 'jetpack_videopress_flash_embed_filter', 7 ); // Needs to be priority 7 to allow Core to oEmbed.
786
787 if ( ! function_exists( 'wp_startswith' ) ) :
788 /**
789 * Check whether a string starts with a specific substring.
790 *
791 * @param string $haystack String we are filtering.
792 * @param string $needle The substring we are looking for.
793 * @return bool
794 */
795 function wp_startswith( $haystack, $needle ) {
796 if ( ! $haystack || ! $needle || ! is_scalar( $haystack ) || ! is_scalar( $needle ) ) {
797 return false;
798 }
799
800 $haystack = (string) $haystack;
801 $needle = (string) $needle;
802
803 return str_starts_with( $haystack, $needle );
804 }
805 endif;
806