Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
35.04% covered (danger)
35.04%
96 / 274
11.11% covered (danger)
11.11%
3 / 27
CRAP
n/a
0 / 0
videopress_is_valid_guid
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
30
videopress_is_valid_preload
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
videopress_get_video_details
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
videopress_download_poster_image
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
create_local_media_library_for_videopress_guid
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
20
videopress_cleanup_media_library
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
20
videopress_cdn_file_url
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
videopress_get_transcoding_status
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
videopress_build_url
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
videopress_create_new_media_item
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
videopress_merge_file_status
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
72
videopress_is_finished_processing
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
videopress_update_meta_data
38.46% covered (danger)
38.46%
5 / 13
0.00% covered (danger)
0.00%
0 / 1
10.83
videopress_is_attachment_without_guid
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
is_videopress_attachment
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
videopress_make_video_get_path
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
videopress_make_media_upload_path
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
videopress_make_resumable_upload_path
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
video_get_info_by_blogpostid
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
1 / 1
9
video_format_done
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
video_image_url_by_guid
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
5.39
videopress_get_post_by_guid
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
4.01
videopress_get_post_id_by_guid
87.50% covered (warning)
87.50%
21 / 24
0.00% covered (danger)
0.00%
0 / 1
3.02
videopress_get_attachment_url
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
jetpack_videopress_flash_embed_filter
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
videopress_is_valid_video_rating
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wp_startswith
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
VIDEOPRESS_PRIVACY
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3use Automattic\Jetpack\Connection\Client;
4
5if ( ! defined( 'ABSPATH' ) ) {
6    exit( 0 );
7}
8
9defined( 'VIDEOPRESS_MIN_WIDTH' ) || define( 'VIDEOPRESS_MIN_WIDTH', 60 );
10defined( '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 */
17abstract 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 */
30function 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 */
43function 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 */
53function 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 */
101function 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 */
134function 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 */
176function 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 */
225function 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 */
236function 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 */
263function 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 */
282function 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 */
307function 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 */
347function 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 */
369function 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 */
407function 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 */
433function 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 */
454function 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 */
470function 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 */
485function 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 * @return stdClass
499 */
500function video_get_info_by_blogpostid( $blog_id, $post_id ) {
501    $post = get_post( $post_id );
502
503    $video_info                  = new stdClass();
504    $video_info->post_id         = 0;
505    $video_info->description     = '';
506    $video_info->title           = '';
507    $video_info->caption         = '';
508    $video_info->blog_id         = $blog_id;
509    $video_info->guid            = null;
510    $video_info->finish_date_gmt = '0000-00-00 00:00:00';
511    $video_info->rating          = null;
512    $video_info->privacy_setting = VIDEOPRESS_PRIVACY::SITE_DEFAULT;
513
514    if ( ! $post ) {
515        return $video_info;
516    }
517
518    $video_info->post_id     = $post_id;
519    $video_info->description = $post->post_content;
520    $video_info->title       = $post->post_title;
521    $video_info->caption     = $post->post_excerpt;
522
523    if ( 'video/videopress' !== $post->post_mime_type ) {
524        return $video_info;
525    }
526
527    // Since this is a VideoPress post, lt's fill out the rest of the object.
528    $video_info->guid = get_post_meta( $post_id, 'videopress_guid', true );
529    $meta             = wp_get_attachment_metadata( $post_id );
530
531    if ( $meta && isset( $meta['videopress'] ) ) {
532        $videopress_meta             = $meta['videopress'];
533        $video_info->rating          = $videopress_meta['rating'] ?? null;
534        $video_info->allow_download  = $videopress_meta['allow_download'] ?? 0;
535        $video_info->display_embed   = $videopress_meta['display_embed'] ?? 0;
536        $video_info->privacy_setting = ! isset( $videopress_meta['privacy_setting'] ) ? VIDEOPRESS_PRIVACY::SITE_DEFAULT : $videopress_meta['privacy_setting'];
537
538        if ( ! empty( $videopress_meta['finished'] ) ) {
539            $video_info->finish_date_gmt = gmdate( 'Y-m-d H:i:s', (int) $videopress_meta['finished'] );
540        }
541    }
542
543    /** Make sure we are keeping some meta keys updated for filtering purposes */
544    if ( get_post_meta( $post_id, 'videopress_rating', true ) !== $video_info->rating ) {
545        update_post_meta( $post_id, 'videopress_rating', $video_info->rating );
546    }
547    if ( get_post_meta( $post_id, 'videopress_privacy_setting', true ) !== $video_info->privacy_setting ) {
548        update_post_meta( $post_id, 'videopress_privacy_setting', $video_info->privacy_setting );
549    }
550
551    return $video_info;
552}
553
554/**
555 * Check that a VideoPress video format has finished processing.
556 *
557 * This uses the info object, because that is what the WPCOM endpoint
558 * uses, however we don't have a complete info object in the same way
559 * WPCOM does, so we pull the meta information out of the post
560 * options instead.
561 *
562 * Note: This mimics the WPCOM function of the same name and helps the media
563 * API endpoint add all needed VideoPress data.
564 *
565 * @param stdClass $info Info object.
566 * @param string   $format Video format.
567 * @return bool
568 */
569function video_format_done( $info, $format ) {
570
571    // Avoids notice when a non-videopress item is found.
572    if ( ! is_object( $info ) ) {
573        return false;
574    }
575
576    $post_id = $info->post_id;
577
578    if ( get_post_mime_type( $post_id ) !== 'video/videopress' ) {
579        return false;
580    }
581
582    $post = get_post( $post_id );
583
584    if ( is_wp_error( $post ) ) {
585        return false;
586    }
587
588    $meta = wp_get_attachment_metadata( $post->ID );
589
590    $video_format = str_replace( array( 'fmt_', 'fmt1_' ), '', $format );
591
592    if ( 'ogg' === $video_format ) {
593        return isset( $meta['videopress']['files']['std']['ogg'] );
594    } else {
595        return isset( $meta['videopress']['files'][ $video_format ]['mp4'] );
596    }
597}
598
599/**
600 * Get the image URL for the given VideoPress GUID
601 *
602 * We look up by GUID, because that is what WPCOM does and this needs to be
603 * parameter compatible with that.
604 *
605 * Note: This mimics the WPCOM function of the same name and helps the media
606 * API endpoint add all needed VideoPress data.
607 *
608 * @param string $guid VideoPress GUID.
609 * @param string $format Video format.
610 * @return string|null The poster image URL, or null if it cannot be resolved.
611 */
612function video_image_url_by_guid( $guid, $format ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
613
614    $post = videopress_get_post_by_guid( $guid );
615
616    if ( is_wp_error( $post ) || ! $post ) {
617        return null;
618    }
619
620    $meta = wp_get_attachment_metadata( $post->ID );
621
622    if ( ! is_array( $meta ) || ! isset( $meta['videopress']['poster'] ) ) {
623        return null;
624    }
625
626    $poster = apply_filters( 'jetpack_photon_url', $meta['videopress']['poster'] );
627
628    return $poster;
629}
630
631/**
632 * Using a GUID, find a post.
633 *
634 * @param string $guid The post guid.
635 * @return WP_Post|false The post for that guid, or false if none is found.
636 */
637function videopress_get_post_by_guid( $guid ) {
638    $cache_key   = 'get_post_by_guid_' . $guid;
639    $cache_group = 'videopress';
640    $cached_post = wp_cache_get( $cache_key, $cache_group );
641
642    if ( is_object( $cached_post ) && 'WP_Post' === get_class( $cached_post ) ) {
643        return $cached_post;
644    }
645
646    $post_id = videopress_get_post_id_by_guid( $guid );
647
648    if ( is_int( $post_id ) ) {
649        $post = get_post( $post_id );
650        wp_cache_set( $cache_key, $post, $cache_group, HOUR_IN_SECONDS );
651
652        return $post;
653    }
654
655    return false;
656}
657
658/**
659 * Using a GUID, find the associated post ID.
660 *
661 * @since 8.4.0
662 * @param string $guid The guid to look for the post ID of.
663 * @return int|false The post ID for that guid, or false if none is found.
664 */
665function videopress_get_post_id_by_guid( $guid ) {
666    $cache_key = 'videopress_get_post_id_by_guid_' . $guid;
667    $cached_id = get_transient( $cache_key );
668
669    if ( is_int( $cached_id ) ) {
670        return $cached_id;
671    }
672
673    $args = array(
674        'post_type'      => 'attachment',
675        'post_mime_type' => 'video/videopress',
676        'post_status'    => 'inherit',
677        'no_found_rows'  => true,
678        'fields'         => 'ids',
679        'meta_query'     => array(
680            array(
681                'key'     => 'videopress_guid',
682                'value'   => $guid,
683                'compare' => '=',
684            ),
685        ),
686    );
687
688    $query = new WP_Query( $args );
689
690    if ( $query->have_posts() ) {
691        $post_id = $query->next_post();
692        set_transient( $cache_key, $post_id, HOUR_IN_SECONDS );
693
694        return $post_id;
695    }
696
697    return false;
698}
699
700/**
701 * From the given VideoPress post_id, return back the appropriate attachment URL.
702 *
703 * When the MP4 hasn't been processed yet or this is not a VideoPress video, this will return null.
704 *
705 * @param int $post_id Post ID of the attachment.
706 * @return string|null
707 */
708function videopress_get_attachment_url( $post_id ) {
709
710    // We only handle VideoPress attachments.
711    if ( get_post_mime_type( $post_id ) !== 'video/videopress' ) {
712        return null;
713    }
714
715    $meta = wp_get_attachment_metadata( $post_id );
716
717    // As of Jetpack 10.3 transcoded video files are reserved for the VideoPress player.
718    // All other video file requests will receive the originally uploaded file, stored on the wpcom cdn.
719    if ( ! isset( $meta['videopress']['original'] ) ) {
720        // Use the original file as the url if it isn't transcoded yet.
721        if ( isset( $meta['original'] ) ) {
722            $return = $meta['original'];
723        } else {
724            // Otherwise, there isn't much we can do.
725            return null;
726        }
727    } else {
728        $return = $meta['videopress']['original'];
729    }
730
731    // If the URL is a string, return it. Otherwise, we shouldn't to avoid errors downstream, so null.
732    return ( is_string( $return ) ) ? $return : null;
733}
734
735/**
736 * Converts VideoPress flash embeds into oEmbed-able URLs.
737 *
738 * Older VideoPress embed depended on Flash, which no longer work,
739 * so let us convert them to an URL that WordPress can oEmbed.
740 *
741 * Note that this file is always loaded via modules/module-extras.php and is not dependent on module status.
742 *
743 * @param string $content the content.
744 * @return string filtered content
745 */
746function jetpack_videopress_flash_embed_filter( $content ) {
747    // This receives data from the `the_content` filter, which unfortunately sometimes has bad content passed along as a param.
748    if ( ! is_string( $content ) ) {
749        return $content;
750    }
751    $regex   = '%<embed[^>]*+>(?:\s*</embed>)?%i';
752    $content = preg_replace_callback(
753        $regex,
754        function ( $matches ) {
755            $embed_code  = $matches[0];
756            $url_matches = array();
757
758            // get video ID from flash URL.
759            $url_matched = preg_match( '/src="http:\/\/v.wordpress.com\/([^"]+)"/', $embed_code, $url_matches );
760
761            if ( $url_matched ) {
762                $video_id = $url_matches[1];
763                return "https://videopress.com/v/$video_id";
764            }
765        },
766        $content
767    );
768    return $content;
769}
770
771/**
772 * Checks if the provided rating string is a valid VideoPress video rating value.
773 *
774 * @param mixed $rating The video rating to validate.
775 * @return bool
776 */
777function videopress_is_valid_video_rating( $rating ) {
778    return in_array( $rating, array( 'G', 'PG-13', 'R-17' ), true );
779}
780
781add_filter( 'the_content', 'jetpack_videopress_flash_embed_filter', 7 ); // Needs to be priority 7 to allow Core to oEmbed.
782
783if ( ! function_exists( 'wp_startswith' ) ) :
784    /**
785     * Check whether a string starts with a specific substring.
786     *
787     * @param string $haystack String we are filtering.
788     * @param string $needle The substring we are looking for.
789     * @return bool
790     */
791    function wp_startswith( $haystack, $needle ) {
792        if ( ! $haystack || ! $needle || ! is_scalar( $haystack ) || ! is_scalar( $needle ) ) {
793            return false;
794        }
795
796        $haystack = (string) $haystack;
797        $needle   = (string) $needle;
798
799        return str_starts_with( $haystack, $needle );
800    }
801endif;