PluginProbe
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels / 2.2.0
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels v2.2.0
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 2.2.0, at includes/youtube-api.php

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