PluginProbe
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels / 1.6.5
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels v1.6.5
2.9.1 2.9.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 2.0.0 2.1.0 2.2.0 2.3.2 2.3.3 2.3.5 2.3.6 2.3.8 2.3.9 2.4.3 All 37 releases
automatic-youtube-gallery / includes / youtube-api.php

youtube-api.php in Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels 1.6.5, at includes/youtube-api.php

708 lines 17.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * A wrapper class for the Youtube Data API v3.
5 *
6 * @link https://plugins360.com
7 * @since 1.0.0
8 *
9 * @package Automatic_YouTube_Gallery
10 */
11
12 // Exit if accessed directly
13 if ( ! defined( 'WPINC' ) ) {
14 die;
15 }
16
17 /**
18 * AYG_YouTube_API class.
19 *
20 * @since 1.0.0
21 */
22 class AYG_YouTube_API {
23
24 /**
25 * The YouTube API Key.
26 *
27 * @since 1.0.0
28 * @access protected
29 * @var string
30 */
31 protected $api_key;
32
33 /**
34 * The YouTube API URLs.
35 *
36 * @since 1.0.0
37 * @access protected
38 * @var array
39 */
40 public $api_urls = array(
41 'playlistItems.list' => 'https://www.googleapis.com/youtube/v3/playlistItems',
42 'channels.list' => 'https://www.googleapis.com/youtube/v3/channels',
43 'search.list' => 'https://www.googleapis.com/youtube/v3/search',
44 'videos.list' => 'https://www.googleapis.com/youtube/v3/videos'
45 );
46
47 /**
48 * Get videos.
49 *
50 * @since 1.0.0
51 * @param array $params Array of query params.
52 * @return mixed
53 */
54 public function get_videos( $params = array() ) {
55 // Get YouTube API Key
56 $general_settings = get_option( 'ayg_general_settings' );
57
58 if ( empty( $general_settings['api_key'] ) ) {
59 return $this->get_error( __( 'YouTube API Key Missing.', 'automatic-youtube-gallery' ) . ' ' . sprintf( __( 'Follow <a href="%s" target="_blank">this guide</a> to get your own API key.', 'automatic-youtube-gallery' ), 'https://plugins360.com/automatic-youtube-gallery/how-to-get-youtube-api-key/' ) );
60 }
61
62 $this->api_key = $general_settings['api_key'];
63
64 // Process response
65 $response = array();
66
67 switch ( $params['type'] ) {
68 case 'playlist':
69 if ( empty( $params['id'] ) ) {
70 return $this->get_error( __( 'YouTube Playlist ID (or) URL is required.', 'automatic-youtube-gallery' ) );
71 }
72
73 $response = $this->request_api_playlist_items( $params );
74 break;
75
76 case 'channel':
77 if ( empty( $params['id'] ) ) {
78 return $this->get_error( __( 'YouTube Channel ID (or) URL is required.', 'automatic-youtube-gallery' ) );
79 }
80
81 // Find playlistId from the channel
82 $params['id'] = $this->parse_youtube_id_from_url( $params['id'], 'channel' );
83 $response = $this->request_api_channels( $params );
84
85 // Get videos using the playlistId
86 if ( ! isset( $response->error ) ) {
87
88 $params['id'] = $response->playlistId;
89 $response = $this->request_api_playlist_items( $params );
90
91 }
92 break;
93
94 case 'username':
95 if ( empty( $params['id'] ) ) {
96 return $this->get_error( __( 'YouTube Account Username is required.', 'automatic-youtube-gallery' ) );
97 }
98
99 // Find playlistId from the channel
100 $params['forUsername'] = $this->parse_youtube_id_from_url( $params['id'], 'username' );
101 unset( $params['id'] );
102
103 $response = $this->request_api_channels( $params );
104
105 // Get videos using the playlistId
106 if ( ! isset( $response->error ) ) {
107
108 $params['id'] = $response->playlistId;
109 $response = $this->request_api_playlist_items( $params );
110
111 }
112 break;
113
114 case 'search':
115 if ( empty( $params['id'] ) ) {
116 return $this->get_error( __( 'Cannot search an empty string. A search term is required.', 'automatic-youtube-gallery' ) );
117 }
118
119 $response = $this->request_api_search( $params );
120 break;
121
122 case 'videos':
123 if ( empty( $params['id'] ) ) {
124 return $this->get_error( __( 'Atleast one YouTube Video ID (or) URL is required.', 'automatic-youtube-gallery' ) );
125 }
126
127 $response = $this->request_api_videos( $params );
128 break;
129
130 case 'livestream':
131 if ( empty( $params['id'] ) ) {
132 return $this->get_error( __( 'YouTube Channel ID (or) URL is required.', 'automatic-youtube-gallery' ) );
133 }
134
135 $response = $this->request_api_livestream( $params );
136 break;
137
138 default: // video
139 if ( empty( $params['id'] ) ) {
140 return $this->get_error( __( 'YouTube Video ID (or) URL is required.', 'automatic-youtube-gallery' ) );
141 }
142
143 $response = $this->request_api_video( $params );
144 break;
145 }
146
147 return $response;
148 }
149
150 /**
151 * Grab the playlist, channel or video ID using the YouTube URL given.
152 *
153 * @since 1.0.0
154 * @access private
155 * @param string $url YouTube URL.
156 * @param string $type Type of the URL (playlist|channel|video).
157 * @return mixed
158 */
159 private function parse_youtube_id_from_url( $url, $type = 'video' ) {
160 $id = $url;
161
162 switch ( $type ) {
163 case 'playlist':
164 if ( preg_match( '/list=(.*)&?\/?/', $url, $matches ) ) {
165 $id = $matches[1];
166 }
167 break;
168
169 case 'channel':
170 $url = parse_url( rtrim( $url, '/' ) );
171
172 if ( isset( $url['path'] ) && preg_match( '/^\/channel\/(([^\/])+?)$/', $url['path'], $matches ) ) {
173 $id = $matches[1];
174 }
175 break;
176
177 case 'username':
178 $url = parse_url( rtrim( $url, '/' ) );
179
180 if ( isset( $url['path'] ) && preg_match( '/^\/user\/(([^\/])+?)$/', $url['path'], $matches ) ) {
181 $id = $matches[1];
182 }
183 break;
184
185 default: // video
186 $url = parse_url( $url );
187
188 if ( array_key_exists( 'host', $url ) ) {
189 if ( 0 === strcasecmp( $url['host'], 'youtu.be' ) ) {
190 $id = substr( $url['path'], 1 );
191 } elseif ( 0 === strcasecmp( $url['host'], 'www.youtube.com' ) ) {
192 if ( isset( $url['query'] ) ) {
193 parse_str( $url['query'], $url['query'] );
194
195 if ( isset( $url['query']['v'] ) ) {
196 $id = $url['query']['v'];
197 }
198 }
199
200 if ( empty( $id ) ) {
201 $url['path'] = explode( '/', substr( $url['path'], 1 ) );
202
203 if ( in_array( $url['path'][0], array( 'e', 'embed', 'v' ) ) ) {
204 $id = $url['path'][1];
205 }
206 }
207 }
208 }
209 }
210
211 return $id;
212 }
213
214 /**
215 * Get videos using playlistItems API.
216 *
217 * @since 1.0.0
218 * @access private
219 * @param array $params Array of query params.
220 * @return stdClass
221 */
222 private function request_api_playlist_items( $params = array() ) {
223 $api_url = $this->get_api_url( 'playlistItems.list' );
224
225 $params['playlistId'] = $this->parse_youtube_id_from_url( $params['id'], 'playlist' );
226
227 $api_params = $this->safe_merge_params(
228 array(
229 'playlistId' => '',
230 'part' => 'id,snippet,contentDetails,status',
231 'maxResults' => 50,
232 'pageToken' => '',
233 'cache' => 0
234 ),
235 $params
236 );
237
238 $api_response = $this->request_api( $api_url, $api_params );
239 if ( isset( $api_response->error ) ) {
240 return $api_response;
241 }
242
243 $videos = $this->parse_videos( $api_response );
244 if ( isset( $videos->error ) ) {
245 return $videos;
246 }
247
248 // Process result
249 $response = new stdClass();
250 $response->page_info = $this->parse_page_info( $api_response );
251 $response->videos = $videos;
252
253 return $response;
254 }
255
256 /**
257 * Find playlistId using channels API.
258 *
259 * @since 1.0.0
260 * @access private
261 * @param array $params Array of query params.
262 * @return mixed
263 */
264 private function request_api_channels( $params = array() ) {
265 $api_url = $this->get_api_url( 'channels.list' );
266
267 $api_params = $this->safe_merge_params(
268 array(
269 'id' => '',
270 'forUsername' => '',
271 'part' => 'contentDetails',
272 'cache' => 0
273 ),
274 $params
275 );
276
277 $api_response = $this->request_api( $api_url, $api_params );
278 if ( isset( $api_response->error ) ) {
279 return $api_response;
280 }
281
282 $items = $api_response->items;
283 if ( ! is_array( $items ) || count( $items ) == 0 ) {
284 return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
285 }
286
287 // Process result
288 $response = new stdClass();
289 $response->playlistId = $items[0]->contentDetails->relatedPlaylists->uploads;
290
291 return $response;
292 }
293
294 /**
295 * Get videos using search API.
296 *
297 * @since 1.0.0
298 * @access private
299 * @param array $params Array of query params.
300 * @return stdClass
301 */
302 private function request_api_search( $params = array() ) {
303 $api_url = $this->get_api_url( 'search.list' );
304
305 $params['q'] = $params['id'];
306
307 if ( ! empty( $params['q'] ) ) {
308 $params['q'] = str_replace( '|', '%7C', $params['q'] );
309 }
310
311 $params['type'] = 'video'; // Overrides user defined type value 'search'
312
313 $api_params = $this->safe_merge_params(
314 array(
315 'q' => '',
316 'channelId' => '',
317 'type' => 'video',
318 'videoEmbeddable' => true,
319 'part' => 'id,snippet',
320 'order' => 'date',
321 'maxResults' => 50,
322 'pageToken' => '',
323 'cache' => 0
324 ),
325 $params
326 );
327
328 $api_response = $this->request_api( $api_url, $api_params );
329 if ( isset( $api_response->error ) ) {
330 return $api_response;
331 }
332
333 $videos = $this->parse_videos( $api_response );
334 if ( isset( $videos->error ) ) {
335 return $videos;
336 }
337
338 // Process result
339 $response = new stdClass();
340 $response->page_info = $this->parse_page_info( $api_response );
341 $response->videos = $videos;
342
343 return $response;
344 }
345
346 /**
347 * Get details of the given video ID.
348 *
349 * @since 1.0.0
350 * @access private
351 * @param array $params Array of query params.
352 * @return stdClass
353 */
354 private function request_api_video( $params = array() ) {
355 $api_url = $this->get_api_url( 'videos.list' );
356
357 $params['id'] = $this->parse_youtube_id_from_url( $params['id'], 'video' );
358
359 $api_params = $this->safe_merge_params(
360 array(
361 'id' => '',
362 'part' => 'id,snippet,contentDetails,status',
363 'cache' => 0
364 ),
365 $params
366 );
367
368 $api_response = $this->request_api( $api_url, $api_params );
369 if ( isset( $api_response->error ) ) {
370 return $api_response;
371 }
372
373 $videos = $this->parse_videos( $api_response );
374
375 // Process result
376 $response = new stdClass();
377 $response->videos = $videos;
378
379 return $response;
380 }
381
382 /**
383 * Get the livestream data.
384 *
385 * @since 1.6.4
386 * @access private
387 * @param array $params Array of query params.
388 * @return stdClass
389 */
390 private function request_api_livestream( $params = array() ) {
391 $response = new stdClass();
392 $response->id = $this->parse_youtube_id_from_url( $params['id'], 'channel' );
393
394 return $response;
395 }
396
397 /**
398 * Get details of the given video IDs.
399 *
400 * @since 1.0.0
401 * @access private
402 * @param array $params Array of query params.
403 * @return stdClass
404 */
405 private function request_api_videos( $params = array() ) {
406 $api_url = $this->get_api_url( 'videos.list' );
407
408 $urls = str_replace( "\n\r", ',', $params['id'] );
409 $urls = str_replace( ' ', ',', $params['id'] );
410 $urls = explode( ',', $urls );
411 $urls = array_filter( $urls );
412
413 $all_ids = array();
414 foreach ( $urls as $url ) {
415 $all_ids[] = $this->parse_youtube_id_from_url( $url, 'video' );
416 }
417 $total_videos = count( $all_ids );
418 $total_pages = ceil( $total_videos / $params['maxResults'] );
419
420 $current_page = isset( $params['pageToken'] ) ? (int) $params['pageToken'] : 1;
421 $current_page = max( $current_page, 1 );
422 $current_page = min( $current_page, $total_pages );
423
424 $offset = ( $current_page - 1 ) * $params['maxResults'];
425 if ( $offset < 0 ) {
426 $offset = 0;
427 }
428
429 $current_ids = array_slice( $all_ids, $offset, $params['maxResults'] );
430 $params['id'] = implode( ',', $current_ids );
431
432 $api_params = $this->safe_merge_params(
433 array(
434 'id' => '',
435 'part' => 'id,snippet,contentDetails,status',
436 'cache' => 0
437 ),
438 $params
439 );
440
441 $api_response = $this->request_api( $api_url, $api_params );
442 if ( isset( $api_response->error ) ) {
443 return $api_response;
444 }
445
446 $videos = $this->parse_videos( $api_response );
447
448 // Process result
449 $response = new stdClass();
450 $response->videos = $videos;
451
452 $response->page_info = array(
453 'videos_found' => $total_videos
454 );
455
456 if ( $current_page > 1 ) {
457 $response->page_info['prev_page_token'] = $current_page - 1;
458 }
459
460 if ( $current_page < $total_pages ) {
461 $response->page_info['next_page_token'] = $current_page + 1;
462 }
463
464 return $response;
465 }
466
467 /**
468 * Get API URL by request.
469 *
470 * @since 1.0.0
471 * @access private
472 * @param array $name
473 * @return string
474 */
475 private function get_api_url( $name ) {
476 return $this->api_urls[ $name ];
477 }
478
479 /**
480 * Request data from the API server.
481 *
482 * @since 1.0.0
483 * @access private
484 * @param string $url YouTube API URL.
485 * @param array $params Array of query params.
486 * @return mixed
487 */
488 private function request_api( $url, $params ) {
489 $params['key'] = $this->api_key;
490
491 // Request data from transients
492 $transient_url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params );
493 $transient_key = 'ayg_' . md5( $transient_url );
494 $transient_data = get_transient( $transient_key );
495
496 if ( ! empty( $transient_data ) ) {
497 return $transient_data;
498 }
499
500 $transient_expiration = 0;
501
502 if ( isset( $params['cache'] ) ) {
503 $transient_expiration = (int) $params['cache'];
504 unset( $params['cache'] );
505 }
506
507 // Request data from API server
508 $q = '';
509 if ( isset( $params['q'] ) ) {
510 $q = $params['q'];
511 unset( $params['q'] );
512 }
513
514 $url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params );
515 if ( ! empty( $q ) ) {
516 $url .= '&q=' . $q;
517 }
518
519 $request = wp_remote_get( $url, array(
520 'headers' => [ 'referer' => home_url() ],
521 ) );
522
523 if ( is_wp_error( $request ) ) {
524 return $this->get_error( $request->get_error_message() );
525 }
526
527 $body = wp_remote_retrieve_body( $request );
528
529 $data = json_decode( $body );
530
531 if ( isset( $data->error ) ) {
532 $message = "Error " . $data->error->code . " " . $data->error->message;
533
534 if ( isset( $data->error->errors[0] ) ) {
535 $message .= " : " . $data->error->errors[0]->reason;
536 }
537
538 return $this->get_error( $message );
539 }
540
541 // Store data in transients
542 if ( $transient_expiration > 0 ) {
543 set_transient( $transient_key, $data, $transient_expiration );
544
545 // Get the current list of transients
546 $transient_keys = get_option( 'ayg_transient_keys', array() );
547 if ( ! is_array( $transient_keys ) ) {
548 $transient_keys = (array) $transient_keys;
549 }
550
551 // Append our new one
552 if ( ! in_array( $transient_key, $transient_keys ) ) {
553 $transient_keys[] = $transient_key;
554 }
555
556 // Save it to the DB
557 update_option( 'ayg_transient_keys', $transient_keys );
558 }
559
560 // Finally return the data
561 return $data;
562 }
563
564 /**
565 * Parse videos from the YouTube API response object.
566 *
567 * @since 1.0.0
568 * @access private
569 * @param object $data YouTube API response object.
570 * @return mixed
571 */
572 private function parse_videos( $data ) {
573 $items = $data->items;
574
575 if ( ! is_array( $items ) || 0 == count( $items ) ) {
576 return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
577 }
578
579 $videos = array();
580
581 foreach ( $items as $item ) {
582 $video = new stdClass();
583
584 // Video ID
585 $video->id = '';
586
587 if ( isset( $item->snippet->resourceId ) && isset( $item->snippet->resourceId->videoId ) ) {
588 $video->id = $item->snippet->resourceId->videoId;
589 } elseif ( isset( $item->contentDetails ) && isset( $item->contentDetails->videoId ) ) {
590 $video->id = $item->contentDetails->videoId;
591 } elseif ( isset( $item->id ) && isset( $item->id->videoId ) ) {
592 $video->id = $item->id->videoId;
593 } elseif ( isset( $item->id ) ) {
594 $video->id = $item->id;
595 }
596
597 // Video title
598 $video->title = $item->snippet->title;
599
600 // Video description
601 $video->description = $item->snippet->description;
602
603 // Video thumbnails
604 if ( isset( $item->snippet->thumbnails ) ) {
605 $video->thumbnails = $item->snippet->thumbnails;
606 }
607
608 // Video publish date
609 $video->published_at = $item->snippet->publishedAt;
610
611 // Push resulting object to the main array
612 $status = 'private';
613
614 if ( isset( $item->status ) && ( 'public' == $item->status->privacyStatus || 'unlisted' == $item->status->privacyStatus ) ) {
615 $status = 'public';
616 }
617
618 if ( isset( $item->snippet->status ) && ( 'public' == $item->snippet->status->privacyStatus || 'unlisted' == $item->snippet->status->privacyStatus ) ) {
619 $status = 'public';
620 }
621
622 if ( 'youtube#searchResult' == $item->kind ) {
623 $status = 'public';
624 }
625
626 if ( 'public' == $status ) {
627 $videos[] = $video;
628 }
629 }
630
631 return $videos;
632 }
633
634 /**
635 * Parse page info from the YouTube API response object.
636 *
637 * @since 1.0.0
638 * @access private
639 * @param object $data YouTube API response object.
640 * @return array
641 */
642 private function parse_page_info( $data ) {
643 $page_info = array();
644
645 // Total count of videos found
646 if ( isset( $data->pageInfo ) && isset( $data->pageInfo->totalResults ) ) {
647 $page_info['videos_found'] = $data->pageInfo->totalResults;
648 }
649
650 // Token for the previous page
651 if ( isset( $data->prevPageToken ) ) {
652 $page_info['prev_page_token'] = $data->prevPageToken;
653 }
654
655 // Token for the next page
656 if ( isset( $data->nextPageToken ) ) {
657 $page_info['next_page_token'] = $data->nextPageToken;
658 }
659
660 return $page_info;
661 }
662
663 /**
664 * Combine user params with known params and fill in defaults when needed.
665 *
666 * @since 1.0.0
667 * @access private
668 * @param array $pairs Entire list of supported params and their defaults.
669 * @param array $params User defined params.
670 * @return array $out Combined and filtered params array.
671 */
672 private function safe_merge_params( $pairs, $params ) {
673 $params = (array) $params;
674 $out = array();
675
676 foreach ( $pairs as $name => $default ) {
677 if ( array_key_exists( $name, $params ) ) {
678 $out[ $name ] = $params[ $name ];
679 } else {
680 $out[ $name ] = $default;
681 }
682
683 if ( empty( $out[ $name ] ) ) {
684 unset( $out[ $name ] );
685 }
686 }
687
688 return $out;
689 }
690
691 /**
692 * Build error object.
693 *
694 * @since 1.0.0
695 * @access private
696 * @param string $message Error message.
697 * @return object Error object.
698 */
699 private function get_error( $message ) {
700 $obj = new stdClass();
701 $obj->error = 1;
702 $obj->error_message = $message;
703
704 return $obj;
705 }
706
707 }
708