Completed
Push — revert/videopress-uploader ( 93d651 )
by
unknown
08:30
created

utility-functions.php ➔ videopress_cleanup_media_library()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 2
nop 0
dl 0
loc 35
rs 8.5806
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 View Code Duplication
	if ( ! empty( $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 View Code Duplication
function videopress_get_attachment_id_by_url( $url ) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
function videopress_download_poster_image( $url, $attachment_id ) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
 * Creates a local media library item of a remote VideoPress video.
157
 *
158
 * @param $guid
159
 * @param int $parent_id
160
 *
161
 * @return int|object
162
 */
163 View Code Duplication
function create_local_media_library_for_videopress_guid( $guid, $parent_id = 0 ) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
	$vp_data = videopress_get_video_details( $guid );
165
	if ( ! $vp_data || is_wp_error( $vp_data ) ) {
166
		return $vp_data;
167
	}
168
169
	$args = array(
170
		'post_date'      => $vp_data->upload_date,
171
		'post_title'     => wp_kses( $vp_data->title, array() ),
172
		'post_content'   => wp_kses( $vp_data->description, array() ),
173
		'post_mime_type' => 'video/videopress',
174
		'guid'           => sprintf( 'https://videopress.com/v/%s', $guid ),
175
	);
176
177
	$attachment_id = wp_insert_attachment( $args, null, $parent_id );
178
179
	if ( ! is_wp_error( $attachment_id ) ) {
180
		update_post_meta( $attachment_id, 'videopress_guid', $guid );
181
		wp_update_attachment_metadata( $attachment_id, array(
182
			'width'  => $vp_data->width,
183
			'height' => $vp_data->height,
184
		) );
185
186
		$thumbnail_id = videopress_download_poster_image( $vp_data->poster, $attachment_id );
187
		update_post_meta( $attachment_id, '_thumbnail_id', $thumbnail_id );
188
	}
189
190
	return $attachment_id;
191
}
192
193
/**
194
 * Helper that will look for VideoPress media items that are more than 30 minutes old,
195
 * that have not had anything attached to them by a wpcom upload and deletes the ghost
196
 * attachment.
197
 *
198
 * These happen primarily because of failed upload attempts.
199
 *
200
 * @return int The number of items that were cleaned up.
201
 */
202
function videopress_cleanup_media_library() {
203
	$query_args = array(
204
		'post_type'      => 'attachment',
205
		'post_status'    => 'inherit',
206
		'post_mime_type' => 'video/videopress',
207
		'meta_query'     => array(
208
			array(
209
				'key'   => 'videopress_status',
210
				'value' => 'new',
211
			),
212
		)
213
	);
214
215
	$query = new WP_Query( $query_args );
216
217
	$cleaned = 0;
218
219
	$now = current_time( 'timestamp' );
220
221
	if ( $query->have_posts() ) {
222
		foreach ( $query->posts as $post ) {
223
			$post_time = strtotime( $post->post_date_gmt );
224
225
			// If the post is older than 30 minutes, it is safe to delete it.
226
			if ( $now - $post_time > MINUTE_IN_SECONDS * 30 ) {
227
				// Force delete the attachment, because we don't want it appearing in the trash.
228
				wp_delete_attachment( $post->ID, true );
229
230
				$cleaned++;
231
			}
232
		}
233
	}
234
235
	return $cleaned;
236
}
237
238
/**
239
 * Return an absolute URI for a given filename and guid on the CDN.
240
 * No check is performed to ensure the guid exists or the file is present. Simple centralized string builder.
241
 *
242
 * @param string $guid     VideoPress identifier
243
 * @param string $filename name of file associated with the guid (video file name or thumbnail file name)
244
 *
245
 * @return string Absolute URL of VideoPress file for the given guid.
246
 */
247
function videopress_cdn_file_url( $guid, $filename ) {
248
	return "https://videos.files.wordpress.com/{$guid}/{$filename}";
249
}
250
251
/**
252
 * Get an array of the transcoding status for the given video post.
253
 *
254
 * @since 4.4
255
 * @param int $post_id
256
 * @return array|bool Returns an array of statuses if this is a VideoPress post, otherwise it returns false.
257
 */
258
function videopress_get_transcoding_status( $post_id ) {
259
	$meta = wp_get_attachment_metadata( $post_id );
260
261
	// If this has not been processed by videopress, we can skip the rest.
262
	if ( !$meta || ! isset( $meta['videopress'] ) ) {
263
		return false;
264
	}
265
266
	$info = (object) $meta['videopress'];
267
268
	$status = array(
269
		'std_mp4' => isset( $info->files_status['std']['mp4'] ) ? $info->files_status['std']['mp4'] : null,
270
		'std_ogg' => isset( $info->files_status['std']['ogg'] ) ? $info->files_status['std']['ogg'] : null,
271
		'dvd_mp4' => isset( $info->files_status['dvd']['mp4'] ) ? $info->files_status['dvd']['mp4'] : null,
272
		'hd_mp4'  => isset( $info->files_status['hd']['mp4'] )  ? $info->files_status['hd']['mp4']  : null,
273
	);
274
275
	return $status;
276
}
277
278
/**
279
 * Get the direct url to the video.
280
 *
281
 * @since 4.4
282
 * @param string $guid
283
 * @return string
284
 */
285
function videopress_build_url( $guid ) {
286
	return 'https://videopress.com/v/' . $guid;
287
}
288
289
/**
290
 * Create an empty videopress media item that will be filled out later by an xmlrpc
291
 * callback from the VideoPress servers.
292
 *
293
 * @since 4.4
294
 * @param string $title
295
 * @return int|WP_Error
296
 */
297
function videopress_create_new_media_item( $title ) {
298
	$post = array(
299
		'post_type'      => 'attachment',
300
		'post_mime_type' => 'video/videopress',
301
		'post_title'     => $title,
302
		'post_content'   => '',
303
	);
304
305
	$media_id = wp_insert_post( $post );
306
307
	add_post_meta( $media_id, 'videopress_status', 'new' );
308
309
	return $media_id;
310
}
311
312
313
/**
314
 * Check to see if a video has completed processing.
315
 *
316
 * @since 4.4
317
 * @param int $post_id
318
 * @return bool
319
 */
320
function videopress_is_finished_processing( $post_id ) {
321
	$post = get_post( $post_id );
322
323
	if ( is_wp_error( $post ) ) {
324
		return false;
325
	}
326
327
	$meta = wp_get_attachment_metadata( $post->ID );
328
329
	if ( ! isset( $meta['videopress'] ) || ! is_array( $meta['videopress'] ) ) {
330
		return false;
331
	}
332
333
	// These are explicitly declared to avoid doing unnecessary loops across two levels of arrays.
334 View Code Duplication
	if ( isset( $meta['videopress']['files_status']['hd'] ) && $meta['videopress']['files_status']['hd'] != 'DONE' ) {
335
		return false;
336
	}
337
338 View Code Duplication
	if ( isset( $meta['videopress']['files_status']['dvd'] ) && $meta['videopress']['files_status']['dvd'] != 'DONE' ) {
339
		return false;
340
	}
341
342 View Code Duplication
	if ( isset( $meta['videopress']['files_status']['std']['mp4'] ) && $meta['videopress']['files_status']['std']['mp4'] != 'DONE' ) {
343
		return false;
344
	}
345
346 View Code Duplication
	if ( isset( $meta['videopress']['files_status']['std']['ogg'] ) && $meta['videopress']['files_status']['std']['ogg'] != 'DONE' ) {
347
		return false;
348
	}
349
350
	return true;
351
}
352
353
354
/**
355
 * Update the meta information  status for the given video post.
356
 *
357
 * @since 4.4
358
 * @param int $post_id
359
 * @return bool
360
 */
361
function videopress_update_meta_data( $post_id ) {
362
363
	$meta = wp_get_attachment_metadata( $post_id );
364
365
	// If this has not been processed by VideoPress, we can skip the rest.
366
	if ( ! $meta || ! isset( $meta['videopress'] ) ) {
367
		return false;
368
	}
369
370
	$info = (object) $meta['videopress'];
371
372
	$result = wp_remote_get( videopress_make_video_get_path( $info->guid ) );
373
374
	if ( is_wp_error( $result ) ) {
375
		return false;
376
	}
377
378
	$response = json_decode( $result['body'], true );
379
380
	// Update the attachment metadata.
381
	$meta['videopress'] = $response;
382
383
	wp_update_attachment_metadata( $post_id, $meta );
384
385
	return true;
386
}
387
388
389
390
/**
391
 * Get the video update path
392
 *
393
 * @since 4.4
394
 * @param string $guid
395
 * @return string
396
 */
397
function videopress_make_video_get_path( $guid ) {
398
	return sprintf(
399
		'%s://%s/rest/v%s/videos/%s',
400
		'https',
401
		JETPACK__WPCOM_JSON_API_HOST,
402
		Jetpack_Client::WPCOM_JSON_API_VERSION,
403
		$guid
404
	);
405
}
406
407
/**
408
 * Get the upload api path.
409
 *
410
 * @since 4.4
411
 * @param int $blog_id The id of the blog we're uploading to.
412
 * @return string
413
 */
414
function videopress_make_media_upload_path( $blog_id ) {
415
	return sprintf(
416
		'https://%s/rest/v1.1/sites/%s/videos/new',
417
		JETPACK__WPCOM_JSON_API_HOST,
418
		$blog_id
419
	);
420
}