Completed
Push — add/videopress-media-endpoint ( 741729 )
by
unknown
11:03
created

utility-functions.php ➔ video_get_info_by_blogpostid()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 2
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * We won't have any videos less than sixty pixels wide. That would be silly.
5
 */
6
defined( 'VIDEOPRESS_MIN_WIDTH' ) or define( 'VIDEOPRESS_MIN_WIDTH', 60 );
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
7
8
/**
9
 * Validate user-supplied guid values against expected inputs
10
 *
11
 * @since 1.1
12
 * @param string $guid video identifier
13
 * @return bool true if passes validation test
14
 */
15
function videopress_is_valid_guid( $guid ) {
16
	if ( ! empty( $guid ) && is_string( $guid ) && strlen( $guid ) === 8 && ctype_alnum( $guid ) ) {
17
		return true;
18
	}
19
	return false;
20
}
21
22
/**
23
 * Get details about a specific video by GUID:
24
 *
25
 * @param $guid string
26
 * @return object
27
 */
28
function videopress_get_video_details( $guid ) {
29
	if ( ! videopress_is_valid_guid( $guid ) ) {
30
		return new WP_Error( 'bad-guid-format', __( 'Invalid Video GUID!', 'jetpack' ) );
31
	}
32
33
	$version  = '1.1';
34
	$endpoint = sprintf( '/videos/%1$s', $guid );
35
	$query_url = sprintf(
36
		'https://public-api.wordpress.com/rest/v%1$s%2$s',
37
		$version,
38
		$endpoint
39
	);
40
41
	// Look for data in our transient. If nothing, let's make a new query.
42
	$data_from_cache = get_transient( 'jetpack_videopress_' . $guid );
43
	if ( false === $data_from_cache ) {
44
		$response = wp_remote_get( esc_url_raw( $query_url ) );
45
		$data     = json_decode( wp_remote_retrieve_body( $response ) );
46
47
		// Cache the response for an hour.
48
		set_transient( 'jetpack_videopress_' . $guid, $data, HOUR_IN_SECONDS );
49
	} else {
50
		$data = $data_from_cache;
51
	}
52
53
	/**
54
	 * Allow functions to modify fetched video details.
55
	 *
56
	 * This filter allows third-party code to modify the return data
57
	 * about a given video.  It may involve swapping some data out or
58
	 * adding new parameters.
59
	 *
60
	 * @since 4.0.0
61
	 *
62
	 * @param object $data The data returned by the WPCOM API. See: https://developer.wordpress.com/docs/api/1.1/get/videos/%24guid/
63
	 * @param string $guid The GUID of the VideoPress video in question.
64
	 */
65
	return apply_filters( 'videopress_get_video_details', $data, $guid );
66
}
67
68
69
/**
70
 * Get an attachment ID given a URL.
71
 *
72
 * Modified from http://wpscholar.com/blog/get-attachment-id-from-wp-image-url/
73
 *
74
 * @todo: Add some caching in here.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
75
 *
76
 * @param string $url
77
 *
78
 * @return int|bool Attachment ID on success, false on failure
79
 */
80
function videopress_get_attachment_id_by_url( $url ) {
81
	$wp_upload_dir = wp_upload_dir();
82
	// Strip out protocols, so it doesn't fail because searching for http: in https: dir.
83
	$dir = set_url_scheme( trailingslashit( $wp_upload_dir['baseurl'] ), 'relative' );
84
85
	// Is URL in uploads directory?
86
	if ( false !== strpos( $url, $dir ) ) {
87
88
		$file = basename( $url );
89
90
		$query_args = array(
91
			'post_type'   => 'attachment',
92
			'post_status' => 'inherit',
93
			'fields'      => 'ids',
94
			'meta_query'  => array(
95
				array(
96
					'key'     => '_wp_attachment_metadata',
97
					'compare' => 'LIKE',
98
					'value'   => $file,
99
				),
100
			)
101
		);
102
103
		$query = new WP_Query( $query_args );
104
105
		if ( $query->have_posts() ) {
106
			foreach ( $query->posts as $attachment_id ) {
107
				$meta          = wp_get_attachment_metadata( $attachment_id );
108
				$original_file = basename( $meta['file'] );
109
				$cropped_files = wp_list_pluck( $meta['sizes'], 'file' );
110
111
				if ( $original_file === $file || in_array( $file, $cropped_files ) ) {
112
					return (int) $attachment_id;
113
				}
114
			}
115
		}
116
117
	}
118
119
	return false;
120
}
121
122
/**
123
 * Similar to `media_sideload_image` -- but returns an ID.
124
 *
125
 * @param $url
126
 * @param $attachment_id
127
 *
128
 * @return int|mixed|object|WP_Error
129
 */
130
function videopress_download_poster_image( $url, $attachment_id ) {
131
	// Set variables for storage, fix file filename for query strings.
132
	preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $url, $matches );
133
	if ( ! $matches ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
134
		return new WP_Error( 'image_sideload_failed', __( 'Invalid image URL', 'jetpack' ) );
135
	}
136
137
	$file_array = array();
138
	$file_array['name']     = basename( $matches[0] );
139
	$file_array['tmp_name'] = download_url( $url );
140
141
	// If error storing temporarily, return the error.
142
	if ( is_wp_error( $file_array['tmp_name'] ) ) {
143
		return $file_array['tmp_name'];
144
	}
145
146
	// Do the validation and storage stuff.
147
	$thumbnail_id = media_handle_sideload( $file_array, $attachment_id, null );
148
149
	// Flag it as poster image, so we can exclude it from display.
150
	update_post_meta( $thumbnail_id, 'videopress_poster_image', 1 );
151
152
	return $thumbnail_id;
153
}
154
155
/**
156
 * Downloads and sets a file to the given attachment.
157
 *
158
 * @param string $url
159
 * @param int $attachment_id
160
 * @return bool|WP_Error
161
 */
162
function videopress_download_video( $url, $attachment_id ) {
163
164
	if ( ! $attachment = get_post( $attachment_id ) )  {
165
		return new WP_Error( 'invalid_attachment', __( 'Could not find video attachment', 'jetpack' ) );
166
	}
167
168
	$tmpfile   = download_url( $url );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 3 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
169
170
	$remote_file_path = parse_url( $url, PHP_URL_PATH );
171
172
	$file_name =  pathinfo( $remote_file_path, PATHINFO_FILENAME ) . '.' . pathinfo( $remote_file_path, PATHINFO_EXTENSION );
173
174
	$time = date( 'YYYY/MM', strtotime( $attachment->post_date ) );
175
176
	if ( ! ( ( $uploads = wp_upload_dir( $time ) ) && false === $uploads['error'] ) ) {
177
		return new WP_Error( 'video_save_failed', __( 'Could not save video', 'jetpack' ) );
178
	}
179
180
	$unique_filename = wp_unique_filename( $uploads['path'], $file_name );
181
182
	$save_path = $uploads['path'] . DIRECTORY_SEPARATOR . $unique_filename;
183
184
	if ( ! @ copy( $tmpfile, $save_path ) ) {
185
		return new WP_Error( 'video_save_failed', __( 'Could not save video', 'jetpack' ) );
186
	}
187
188
	unlink( $tmpfile );
189
190
	update_attached_file( $attachment_id, $save_path );
191
192
	return true;
193
}
194
195
/**
196
 * Creates a local media library item of a remote VideoPress video.
197
 *
198
 * @param $guid
199
 * @param int $parent_id
200
 *
201
 * @return int|object
202
 */
203
function create_local_media_library_for_videopress_guid( $guid, $parent_id = 0 ) {
204
	$vp_data = videopress_get_video_details( $guid );
205
	if ( ! $vp_data || is_wp_error( $vp_data ) ) {
206
		return $vp_data;
207
	}
208
209
	$args = array(
210
		'post_date'      => $vp_data->upload_date,
211
		'post_title'     => wp_kses( $vp_data->title, array() ),
212
		'post_content'   => wp_kses( $vp_data->description, array() ),
213
		'post_mime_type' => 'video/videopress',
214
		'guid'           => sprintf( 'https://videopress.com/v/%s', $guid ),
215
	);
216
217
	$attachment_id = wp_insert_attachment( $args, null, $parent_id );
218
219
	if ( ! is_wp_error( $attachment_id ) ) {
220
		update_post_meta( $attachment_id, 'videopress_guid', $guid );
221
		wp_update_attachment_metadata( $attachment_id, array(
222
			'width'  => $vp_data->width,
223
			'height' => $vp_data->height,
224
		) );
225
226
		$thumbnail_id = videopress_download_poster_image( $vp_data->poster, $attachment_id );
227
		update_post_meta( $attachment_id, '_thumbnail_id', $thumbnail_id );
228
	}
229
230
	return $attachment_id;
231
}
232
233
/**
234
 * Helper that will look for VideoPress media items that are more than 30 minutes old,
235
 * that have not had anything attached to them by a wpcom upload and deletes the ghost
236
 * attachment.
237
 *
238
 * These happen primarily because of failed upload attempts.
239
 *
240
 * @return int The number of items that were cleaned up.
241
 */
242
function videopress_cleanup_media_library() {
243
244
	// Disable this job for now.
245
	return 0;
246
	$query_args = array(
0 ignored issues
show
Unused Code introduced by
$query_args = array('pos...', 'value' => 'new'))); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
247
		'post_type'      => 'attachment',
248
		'post_status'    => 'inherit',
249
		'post_mime_type' => 'video/videopress',
250
		'meta_query'     => array(
251
			array(
252
				'key'   => 'videopress_status',
253
				'value' => 'new',
254
			),
255
		)
256
	);
257
258
	$query = new WP_Query( $query_args );
259
260
	$cleaned = 0;
261
262
	$now = current_time( 'timestamp' );
263
264
	if ( $query->have_posts() ) {
265
		foreach ( $query->posts as $post ) {
266
			$post_time = strtotime( $post->post_date_gmt );
267
268
			// If the post is older than 30 minutes, it is safe to delete it.
269
			if ( $now - $post_time > MINUTE_IN_SECONDS * 30 ) {
270
				// Force delete the attachment, because we don't want it appearing in the trash.
271
				wp_delete_attachment( $post->ID, true );
272
273
				$cleaned++;
274
			}
275
		}
276
	}
277
278
	return $cleaned;
279
}
280
281
/**
282
 * Return an absolute URI for a given filename and guid on the CDN.
283
 * No check is performed to ensure the guid exists or the file is present. Simple centralized string builder.
284
 *
285
 * @param string $guid     VideoPress identifier
286
 * @param string $filename name of file associated with the guid (video file name or thumbnail file name)
287
 *
288
 * @return string Absolute URL of VideoPress file for the given guid.
289
 */
290
function videopress_cdn_file_url( $guid, $filename ) {
291
	return "https://videos.files.wordpress.com/{$guid}/{$filename}";
292
}
293
294
/**
295
 * Get an array of the transcoding status for the given video post.
296
 *
297
 * @since 4.4
298
 * @param int $post_id
299
 * @return array|bool Returns an array of statuses if this is a VideoPress post, otherwise it returns false.
300
 */
301
function videopress_get_transcoding_status( $post_id ) {
302
	$meta = wp_get_attachment_metadata( $post_id );
303
304
	// If this has not been processed by videopress, we can skip the rest.
305
	if ( ! $meta || ! isset( $meta['file_statuses'] ) ) {
306
		return false;
307
	}
308
309
	$info = (object) $meta['file_statuses'];
310
311
	$status = array(
312
		'std_mp4' => isset( $info->mp4 ) ? $info->mp4 : null,
313
		'std_ogg' => isset( $info->ogg ) ? $info->ogg : null,
314
		'dvd_mp4' => isset( $info->dvd ) ? $info->dvd : null,
315
		'hd_mp4'  => isset( $info->hd )  ? $info->hd : null,
316
	);
317
318
	return $status;
319
}
320
321
/**
322
 * Get the direct url to the video.
323
 *
324
 * @since 4.4
325
 * @param string $guid
326
 * @return string
327
 */
328
function videopress_build_url( $guid ) {
329
330
	// No guid, no videopress url.
331
	if ( ! $guid ) {
332
		return '';
333
	}
334
335
	return 'https://videopress.com/v/' . $guid;
336
}
337
338
/**
339
 * Create an empty videopress media item that will be filled out later by an xmlrpc
340
 * callback from the VideoPress servers.
341
 *
342
 * @since 4.4
343
 * @param string $title
344
 * @return int|WP_Error
345
 */
346
function videopress_create_new_media_item( $title, $guid = null ) {
347
	$post = array(
348
		'post_type'      => 'attachment',
349
		'post_mime_type' => 'video/videopress',
350
		'post_title'     => $title,
351
		'post_content'   => '',
352
		'guid'           => videopress_build_url( $guid ),
353
	);
354
355
	$media_id = wp_insert_post( $post );
356
357
	add_post_meta( $media_id, 'videopress_status', 'initiated' );
358
359
	add_post_meta( $media_id, 'videopress_guid', $guid );
360
361
	return $media_id;
362
}
363
364
365
/**
366
 * @param array $current_status
367
 * @param array $new_meta
368
 * @return array
369
 */
370
function videopress_merge_file_status( $current_status, $new_meta ) {
371
	$new_statuses = array();
372
373 View Code Duplication
	if ( isset( $new_meta['videopress']['files_status']['hd'] ) ) {
374
		$new_statuses['hd'] = $new_meta['videopress']['files_status']['hd'];
375
	}
376
377 View Code Duplication
	if ( isset( $new_meta['videopress']['files_status']['dvd'] ) ) {
378
		$new_statuses['dvd'] = $new_meta['videopress']['files_status']['dvd'];
379
	}
380
381 View Code Duplication
	if ( isset( $new_meta['videopress']['files_status']['std']['mp4'] ) ) {
382
		$new_statuses['mp4'] = $new_meta['videopress']['files_status']['std']['mp4'];
383
	}
384
385 View Code Duplication
	if ( isset( $new_meta['videopress']['files_status']['std']['ogg'] ) ) {
386
		$new_statuses['ogg'] = $new_meta['videopress']['files_status']['std']['ogg'];
387
	}
388
389
	foreach ( $new_statuses as $format => $status ) {
390
		if ( ! isset( $current_status[ $format ] ) ) {
391
			$current_status[ $format ] = $status;
392
			continue;
393
		}
394
395
		if ( $current_status[ $format ] !== 'DONE' ) {
396
			$current_status[ $format ] = $status;
397
		}
398
	}
399
400
	return $current_status;
401
}
402
403
/**
404
 * Check to see if a video has completed processing.
405
 *
406
 * @since 4.4
407
 * @param int $post_id
408
 * @return bool
409
 */
410
function videopress_is_finished_processing( $post_id ) {
411
	$post = get_post( $post_id );
412
413
	if ( is_wp_error( $post ) ) {
414
		return false;
415
	}
416
417
	$meta = wp_get_attachment_metadata( $post->ID );
418
419
	if ( ! isset( $meta['file_statuses'] ) || ! is_array( $meta['file_statuses'] ) ) {
420
		return false;
421
	}
422
423
	$check_statuses = array( 'hd', 'dvd', 'mp4', 'ogg' );
424
425
	foreach ( $check_statuses as $status ) {
426
		if ( ! isset( $meta['file_statuses'][ $status ] ) || $meta['file_statuses'][ $status ] != 'DONE' ) {
427
			return false;
428
		}
429
	}
430
431
	return true;
432
}
433
434
435
/**
436
 * Update the meta information  status for the given video post.
437
 *
438
 * @since 4.4
439
 * @param int $post_id
440
 * @return bool
441
 */
442
function videopress_update_meta_data( $post_id ) {
443
444
	$meta = wp_get_attachment_metadata( $post_id );
445
446
	// If this has not been processed by VideoPress, we can skip the rest.
447
	if ( ! $meta || ! isset( $meta['videopress'] ) ) {
448
		return false;
449
	}
450
451
	$info = (object) $meta['videopress'];
452
453
	$args = array(
454
		// 'sslverify' => false,
455
	);
456
457
	$result = wp_remote_get( videopress_make_video_get_path( $info->guid ), $args );
458
459
	if ( is_wp_error( $result ) ) {
460
		return false;
461
	}
462
463
	$response = json_decode( $result['body'], true );
464
465
	// Update the attachment metadata.
466
	$meta['videopress'] = $response;
467
468
	wp_update_attachment_metadata( $post_id, $meta );
469
470
	return true;
471
}
472
473
/**
474
 * Check to see if this is a VideoPress post that hasn't had a guid set yet.
475
 *
476
 * @param int $post_id
477
 * @return bool
478
 */
479
function videopress_is_attachment_without_guid( $post_id ) {
480
	$post = get_post( $post_id );
481
482
	if ( is_wp_error( $post ) ) {
483
		return false;
484
	}
485
486
	if ( $post->post_mime_type !== 'video/videopress' ) {
487
		return false;
488
	}
489
490
	$videopress_guid = get_post_meta( $post_id, 'videopress_guid', true );
491
492
	if ( $videopress_guid ) {
493
		return false;
494
	}
495
496
	return true;
497
}
498
499
/**
500
 * Check to see if this is a VideoPress attachment.
501
 *
502
 * @param int $post_id
503
 * @return bool
504
 */
505
function is_videopress_attachment( $post_id ) {
506
	$post = get_post( $post_id );
507
508
	if ( is_wp_error( $post ) ) {
509
		return false;
510
	}
511
512
	if ( $post->post_mime_type !== 'video/videopress' ) {
513
		return false;
514
	}
515
516
	return true;
517
}
518
519
/**
520
 * Get the video update path
521
 *
522
 * @since 4.4
523
 * @param string $guid
524
 * @return string
525
 */
526
function videopress_make_video_get_path( $guid ) {
527
	return sprintf(
528
		'%s://%s/rest/v%s/videos/%s',
529
		'https',
530
		JETPACK__WPCOM_JSON_API_HOST,
531
		Jetpack_Client::WPCOM_JSON_API_VERSION,
532
		$guid
533
	);
534
}
535
536
/**
537
 * Get the upload api path.
538
 *
539
 * @since 4.4
540
 * @param int $blog_id The id of the blog we're uploading to.
541
 * @return string
542
 */
543
function videopress_make_media_upload_path( $blog_id ) {
544
	return sprintf(
545
		'https://public-api.wordpress.com/rest/v1.1/sites/%s/media/new',
546
		$blog_id
547
	);
548
}
549
550
/**
551
 * This is a mock of the internal VideoPress method, which is meant to duplicate the functionality
552
 * of the WPCOM API, so that the Jetpack REST API returns the same data with no modifications.
553
 *
554
 * @param int $blog_id
555
 * @param int $post_id
556
 * @return bool|stdClass
557
 */
558
function video_get_info_by_blogpostid( $blog_id, $post_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $blog_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
559
	$post = get_post( $post_id );
560
561
	if ( is_wp_error( $post ) ) {
562
		return false;
563
	}
564
565
	if ( $post->post_mime_type !== 'video/videopress' ) {
566
		return false;
567
	}
568
569
	$video_info = new stdClass();
570
	$video_info->guid = get_post_meta( $post_id, 'videopress_guid', true );
571
	$video_info->finish_date_gmt = '0000-00-00 00:00:00';
572
573
	if ( videopress_is_finished_processing( $post_id ) ) {
574
		$video_info->finish_date_gmt = date( 'Y-m-d H:i:s' );
575
	}
576
577
	return $video_info;
578
}