PluginProbe
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels / 2.6.3
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels v2.6.3
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
← All changes | includes/youtube-api.php +514 -195 1.1.02.6.3 View file →
@@ -2,13 +2,12 @@
2 2
3 3 /**
4 4 * A wrapper class for the Youtube Data API v3.
5 5 *
6 - * @link https://plugins360.com
7 - * @since 1.0.0
6 + * @link https://plugins360.com
7 + * @since 1.0.0
8 8 *
9 - * @package Automatic_YouTube_Gallery
10 - * @subpackage Automatic_YouTube_Gallery/includes
9 + * @package Automatic_YouTube_Gallery
11 10 */
12 11
13 12 // Exit if accessed directly
14 13 if ( ! defined( 'WPINC' ) ) {
@@ -31,8 +30,26 @@
31 30 */
32 31 protected $api_key;
33 32
34 33 /**
34 + * Array of query params.
35 + *
36 + * @since 2.5.7
37 + * @access protected
38 + * @var array
39 + */
40 + protected $params = array();
41 +
42 + /**
43 + * Is development mode enabled?
44 + *
45 + * @since 2.3.0
46 + * @access protected
47 + * @var bool
48 + */
49 + protected $is_development_mode = false;
50 +
51 + /**
35 52 * The YouTube API URLs.
36 53 *
37 54 * @since 1.0.0
38 55 * @access protected
@@ -48,29 +65,39 @@
48 65 /**
49 66 * Get videos.
50 67 *
51 68 * @since 1.0.0
52 - * @param array $params Array of query params.
69 + * @param array $params Array of query params.
53 70 * @return mixed
54 71 */
55 - public function get_videos( $params = array() ) {
56 -
72 + public function query( $params = array() ) {
57 73 // Get YouTube API Key
58 74 $general_settings = get_option( 'ayg_general_settings' );
59 75
60 76 if ( empty( $general_settings['api_key'] ) ) {
61 - 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/' ) );
77 + return $this->get_error( __( 'YouTube API key not found.', 'automatic-youtube-gallery' ) . ' ' . sprintf( __( 'Kindly follow this URL <a href="%s" target="_blank" rel="noopener noreferrer">this guide</a> to get your own API key.', 'automatic-youtube-gallery' ), 'https://plugins360.com/automatic-youtube-gallery/how-to-get-youtube-api-key/' ) );
62 78 }
63 79
64 80 $this->api_key = $general_settings['api_key'];
81 + $this->params = $params;
65 82
66 - // Process response
83 + // Is development mode enabled?
84 + if ( isset( $general_settings['development_mode'] ) && ! empty( $general_settings['development_mode'] ) ) {
85 + $this->is_development_mode = true;
86 + }
87 +
88 + // Process output
67 89 $response = array();
68 90
91 + if ( ! empty( $params['searchTerm'] ) ) {
92 + $response = $this->get_videos_from_db( $params );
93 + return $response;
94 + }
95 +
69 96 switch ( $params['type'] ) {
70 97 case 'playlist':
71 - if ( empty( $params['id'] ) ) {
72 - return $this->get_error( __( 'YouTube playlist URL is required.', 'automatic-youtube-gallery' ) );
98 + if ( empty( $params['src'] ) ) {
99 + return $this->get_error( __( 'YouTube Playlist ID (or) URL is required.', 'automatic-youtube-gallery' ) );
73 100 }
74 101
75 102 $response = $this->request_api_playlist_items( $params );
76 103 break;
@@ -75,47 +102,58 @@
75 102 $response = $this->request_api_playlist_items( $params );
76 103 break;
77 104
78 105 case 'channel':
106 + if ( empty( $params['src'] ) ) {
107 + return $this->get_error( __( 'YouTube Channel ID (or) or a YouTube Video URL from the Channel is required.', 'automatic-youtube-gallery' ) );
108 + }
109 +
110 + $params['id'] = $this->get_channel_id( $params );
111 +
79 112 if ( empty( $params['id'] ) ) {
80 - return $this->get_error( __( 'YouTube channel URL is required.', 'automatic-youtube-gallery' ) );
113 + return $this->get_error( __( 'Invalid YouTube Channel ID.', 'automatic-youtube-gallery' ) );
81 114 }
82 115
83 - // Find playlistId from the channel
84 - $params['id'] = $this->parse_youtube_id_from_url( $params['id'], 'channel' );
85 - $response = $this->request_api_channels( $params );
116 + // Get playlist id from the channel
117 + $playlist_id = $this->get_playlist_id( $params );
86 118
87 - // Get videos using the playlistId
88 - if ( ! isset( $response->error ) ) {
119 + if ( is_object( $playlist_id ) && isset( $playlist_id->error ) ) {
120 + return $playlist_id;
121 + }
89 122
90 - $params['id'] = $response->playlistId;
91 - $response = $this->request_api_playlist_items( $params );
123 + if ( empty( $playlist_id ) ) {
124 + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
125 + }
92 126
93 - }
127 + // Get videos using the playlist id
128 + $params['src'] = $playlist_id;
129 + $response = $this->request_api_playlist_items( $params );
94 130 break;
95 131
96 132 case 'username':
97 - if ( empty( $params['id'] ) ) {
98 - return $this->get_error( __( 'YouTube account username is required.', 'automatic-youtube-gallery' ) );
133 + if ( empty( $params['src'] ) ) {
134 + return $this->get_error( __( 'YouTube Account Username is required.', 'automatic-youtube-gallery' ) );
99 135 }
100 136
101 - // Find playlistId from the channel
102 - $params['forUsername'] = $this->parse_youtube_id_from_url( $params['id'], 'username' );
103 - unset( $params['id'] );
137 + // Get playlist id from the channel
138 + $params['forUsername'] = $this->parse_youtube_id_from_url( $params['src'], 'username' );
139 + $playlist_id = $this->get_playlist_id( $params );
104 140
105 - $response = $this->request_api_channels( $params );
141 + if ( is_object( $playlist_id ) && isset( $playlist_id->error ) ) {
142 + return $playlist_id;
143 + }
106 144
107 - // Get videos using the playlistId
108 - if ( ! isset( $response->error ) ) {
145 + if ( empty( $playlist_id ) ) {
146 + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
147 + }
109 148
110 - $params['id'] = $response->playlistId;
111 - $response = $this->request_api_playlist_items( $params );
112 -
113 - }
149 + // Get videos using the playlist id
150 + $params['src'] = $playlist_id;
151 + $response = $this->request_api_playlist_items( $params );
114 152 break;
115 153
116 154 case 'search':
117 - if ( empty( $params['id'] ) ) {
155 + if ( empty( $params['src'] ) ) {
118 156 return $this->get_error( __( 'Cannot search an empty string. A search keyword is required.', 'automatic-youtube-gallery' ) );
119 157 }
120 158
121 159 $response = $this->request_api_search( $params );
@@ -121,18 +159,33 @@
121 159 $response = $this->request_api_search( $params );
122 160 break;
123 161
124 162 case 'videos':
125 - if ( empty( $params['id'] ) ) {
126 - return $this->get_error( __( 'Atleast one YouTube video URL is required.', 'automatic-youtube-gallery' ) );
163 + if ( empty( $params['src'] ) ) {
164 + return $this->get_error( __( 'Atleast one YouTube Video ID (or) URL is required.', 'automatic-youtube-gallery' ) );
127 165 }
128 166
129 167 $response = $this->request_api_videos( $params );
130 168 break;
131 169
170 + case 'livestream':
171 + if ( empty( $params['src'] ) ) {
172 + return $this->get_error( __( 'YouTube Channel ID (or) or a YouTube Video URL from the Channel is required.', 'automatic-youtube-gallery' ) );
173 + }
174 +
175 + $params['channelId'] = $this->get_channel_id( $params );
176 +
177 + if ( empty( $params['channelId'] ) ) {
178 + return $this->get_error( __( 'Invalid YouTube Channel ID.', 'automatic-youtube-gallery' ) );
179 + }
180 +
181 + // Get live video using the channel id
182 + $response = $this->request_api_live_video( $params );
183 + break;
184 +
132 185 default: // video
133 - if ( empty( $params['id'] ) ) {
134 - return $this->get_error( __( 'YouTube video URL is required.', 'automatic-youtube-gallery' ) );
186 + if ( empty( $params['src'] ) ) {
187 + return $this->get_error( __( 'YouTube Video ID (or) URL is required.', 'automatic-youtube-gallery' ) );
135 188 }
136 189
137 190 $response = $this->request_api_video( $params );
138 191 break;
@@ -138,9 +191,8 @@
138 191 break;
139 192 }
140 193
141 194 return $response;
142 -
143 195 }
144 196
145 197 /**
146 198 * Grab the playlist, channel or video ID using the YouTube URL given.
@@ -151,19 +203,23 @@
151 203 * @param string $type Type of the URL (playlist|channel|video).
152 204 * @return mixed
153 205 */
154 206 private function parse_youtube_id_from_url( $url, $type = 'video' ) {
207 + $url = trim( $url );
208 + $id = $url;
155 209
156 - $id = $url;
157 -
158 210 switch ( $type ) {
159 211 case 'playlist':
160 - if ( preg_match( '/list=(.*)&?\/?/', $url, $matches ) ) {
212 + if ( preg_match( '/[?&]list=([^&]+)/', $url, $matches ) ) {
161 213 $id = $matches[1];
162 214 }
163 215 break;
164 216
165 217 case 'channel':
218 + if ( wp_http_validate_url( $id ) ) {
219 + $id = '';
220 + }
221 +
166 222 $url = parse_url( rtrim( $url, '/' ) );
167 223
168 224 if ( isset( $url['path'] ) && preg_match( '/^\/channel\/(([^\/])+?)$/', $url['path'], $matches ) ) {
169 225 $id = $matches[1];
@@ -178,44 +234,168 @@
178 234 }
179 235 break;
180 236
181 237 default: // video
238 + if ( wp_http_validate_url( $id ) ) {
239 + $id = '';
240 + }
241 +
182 242 $url = parse_url( $url );
183 243
184 - if ( array_key_exists( 'host', $url ) ) {
185 -
244 + if ( array_key_exists( 'host', $url ) ) {
186 245 if ( 0 === strcasecmp( $url['host'], 'youtu.be' ) ) {
187 246 $id = substr( $url['path'], 1 );
188 - } elseif ( 0 === strcasecmp( $url['host'], 'www.youtube.com' ) ) {
189 -
247 + } elseif ( 0 === strcasecmp( $url['host'], 'www.youtube.com' ) || 0 === strcasecmp( $url['host'], 'youtube.com' ) ) {
190 248 if ( isset( $url['query'] ) ) {
191 -
192 249 parse_str( $url['query'], $url['query'] );
193 250
194 251 if ( isset( $url['query']['v'] ) ) {
195 252 $id = $url['query']['v'];
196 253 }
197 -
198 254 }
199 255
200 256 if ( empty( $id ) ) {
201 -
202 257 $url['path'] = explode( '/', substr( $url['path'], 1 ) );
203 -
204 - if ( in_array( $url['path'][0], array( 'e', 'embed', 'v' ) ) ) {
258 + if ( in_array( $url['path'][0], array( 'e', 'embed', 'v', 'shorts', 'live' ) ) ) {
205 259 $id = $url['path'][1];
206 260 }
261 + }
262 + }
263 + }
264 + }
207 265
208 - }
266 + return $id;
267 + }
209 268
210 - }
269 + /**
270 + * Get the channel ID.
271 + *
272 + * @since 2.0.0
273 + * @access private
274 + * @param array $params Array of query params.
275 + * @return string
276 + */
277 + private function get_channel_id( $params = array() ) {
278 + // Parse channel ID from URL: https://www.youtube.com/channel/XXXXXXXXXX
279 + $id = $this->parse_youtube_id_from_url( $params['src'], 'channel' );
211 280
212 - }
281 + if ( empty( $id ) ) {
282 + // Get channel ID from a Video URL: https://www.youtube.com/watch?v=XXXXXXXXXX
283 + $video_id = $this->parse_youtube_id_from_url( $params['src'], 'video' );
213 284
285 + // Request from cache
286 + $channel_ids = get_option( 'ayg_channel_ids', array() );
287 + if ( ! is_array( $channel_ids ) ) {
288 + $channel_ids = (array) $channel_ids;
289 + }
290 +
291 + if ( isset( $channel_ids[ $video_id ] ) && ! empty( $channel_ids[ $video_id ] ) ) {
292 + return $channel_ids[ $video_id ];
293 + }
294 +
295 + // Request from API
296 + $api_url = $this->get_api_url( 'videos.list' );
297 +
298 + $params['id'] = $video_id;
299 +
300 + $api_params = $this->safe_merge_params(
301 + array(
302 + 'id' => '',
303 + 'part' => 'id,snippet,contentDetails,status',
304 + 'cache' => 0
305 + ),
306 + $params
307 + );
308 +
309 + $api_response = $this->request_api( $api_url, $api_params, 'channel_id' );
310 + if ( isset( $api_response->error ) ) {
311 + return $id;
312 + }
313 +
314 + $videos = $this->parse_videos( $api_response );
315 + if ( isset( $videos->error ) ) {
316 + return $id;
317 + }
318 +
319 + // Process output
320 + if ( $id = $videos[0]->channel_id ) {
321 + // Store in cache
322 + $channel_ids[ $video_id ] = $id;
323 + update_option( 'ayg_channel_ids', $channel_ids );
324 + }
214 325 }
215 326
216 - return $id;
327 + return $id;
328 + }
217 329
330 + /**
331 + * Get playlist id using channels API.
332 + *
333 + * @since 1.0.0
334 + * @access private
335 + * @param array $params Array of query params.
336 + * @return mixed
337 + */
338 + private function get_playlist_id( $params = array() ) {
339 + // Request from cache
340 + $playlist_ids = get_option( 'ayg_playlist_ids', array() );
341 + if ( ! is_array( $playlist_ids ) ) {
342 + $playlist_ids = (array) $playlist_ids;
343 + }
344 +
345 + $key = '';
346 +
347 + if ( isset( $params['forUsername'] ) && ! empty( $params['forUsername'] ) ) {
348 + $key = $params['forUsername'];
349 + }
350 +
351 + if ( isset( $params['id'] ) && ! empty( $params['id'] ) ) {
352 + unset( $params['forUsername'] );
353 + $key = $params['id'];
354 + }
355 +
356 + if ( isset( $playlist_ids[ $key ] ) && ! empty( $playlist_ids[ $key ] ) ) {
357 + return $playlist_ids[ $key ];
358 + }
359 +
360 + // Request from API
361 + $api_url = $this->get_api_url( 'channels.list' );
362 +
363 + $api_params = $this->safe_merge_params(
364 + array(
365 + 'id' => '',
366 + 'forUsername' => '',
367 + 'part' => 'contentDetails',
368 + 'cache' => 0
369 + ),
370 + $params
371 + );
372 +
373 + $api_response = $this->request_api( $api_url, $api_params, 'playlist_id' );
374 + if ( isset( $api_response->error ) ) {
375 + return $api_response;
376 + }
377 +
378 + if ( ! isset( $api_response->items ) ) {
379 + return false;
380 + }
381 +
382 + $items = $api_response->items;
383 + if ( ! is_array( $items ) || count( $items ) == 0 ) {
384 + return false;
385 + }
386 +
387 + // Process output
388 + if ( $id = $items[0]->contentDetails->relatedPlaylists->uploads ) {
389 + // Store in cache
390 + $playlist_ids[ $key ] = $id;
391 + update_option( 'ayg_playlist_ids', $playlist_ids );
392 +
393 + // Return
394 + return $id;
395 + }
396 +
397 + return false;
218 398 }
219 399
220 400 /**
221 401 * Get videos using playlistItems API.
@@ -225,12 +405,11 @@
225 405 * @param array $params Array of query params.
226 406 * @return stdClass
227 407 */
228 408 private function request_api_playlist_items( $params = array() ) {
229 -
230 409 $api_url = $this->get_api_url( 'playlistItems.list' );
231 410
232 - $params['playlistId'] = $this->parse_youtube_id_from_url( $params['id'], 'playlist' );
411 + $params['playlistId'] = $this->parse_youtube_id_from_url( $params['src'], 'playlist' );
233 412
234 413 $api_params = $this->safe_merge_params(
235 414 array(
236 415 'playlistId' => '',
@@ -251,59 +430,112 @@
251 430 if ( isset( $videos->error ) ) {
252 431 return $videos;
253 432 }
254 433
255 - // Process result
434 + // Process output
256 435 $response = new stdClass();
257 436 $response->page_info = $this->parse_page_info( $api_response );
258 437 $response->videos = $videos;
259 438
260 - return $response;
439 + return $response;
440 + }
441 +
442 + /**
443 + * Get videos using search API.
444 + *
445 + * @since 1.0.0
446 + * @access private
447 + * @param array $params Array of query params.
448 + * @return stdClass
449 + */
450 + private function request_api_search( $params = array() ) {
451 + $api_url = $this->get_api_url( 'search.list' );
452 +
453 + $params['q'] = $params['src'];
454 +
455 + if ( ! empty( $params['q'] ) ) {
456 + $params['q'] = str_replace( '|', '%7C', $params['q'] );
457 + }
458 +
459 + $params['type'] = 'video'; // Overrides user defined type value 'search'
460 +
461 + $api_params = $this->safe_merge_params(
462 + array(
463 + 'q' => '',
464 + 'channelId' => '',
465 + 'type' => 'video',
466 + 'videoEmbeddable' => true,
467 + 'part' => 'id,snippet',
468 + 'order' => 'date',
469 + 'maxResults' => 50,
470 + 'pageToken' => '',
471 + 'cache' => 0
472 + ),
473 + $params
474 + );
261 475
262 - }
476 + $api_response = $this->request_api( $api_url, $api_params );
477 + if ( isset( $api_response->error ) ) {
478 + return $api_response;
479 + }
263 480
481 + $videos = $this->parse_videos( $api_response );
482 + if ( isset( $videos->error ) ) {
483 + return $videos;
484 + }
485 +
486 + // Process output
487 + $response = new stdClass();
488 + $response->page_info = $this->parse_page_info( $api_response );
489 + $response->videos = $videos;
490 +
491 + return $response;
492 + }
493 +
264 494 /**
265 - * Find playlistId using channels API.
495 + * Get live video using search API.
266 496 *
267 - * @since 1.0.0
497 + * @since 2.3.7
268 498 * @access private
269 499 * @param array $params Array of query params.
270 500 * @return mixed
271 501 */
272 - private function request_api_channels( $params = array() ) {
502 + private function request_api_live_video( $params = array() ) {
503 + $api_url = $this->get_api_url( 'search.list' );
273 504
274 - $api_url = $this->get_api_url( 'channels.list' );
505 + $params['type'] = 'video'; // Overrides user defined type value 'livestream'
275 506
276 507 $api_params = $this->safe_merge_params(
277 508 array(
278 - 'id' => '',
279 - 'forUsername' => '',
280 - 'part' => 'contentDetails',
281 - 'cache' => 0
509 + 'type' => 'video',
510 + 'eventType' => 'live',
511 + 'part' => 'snippet',
512 + 'channelId' => '',
513 + 'cache' => 0
282 514 ),
283 515 $params
284 516 );
285 517
286 - $api_response = $this->request_api( $api_url, $api_params );
518 + $api_response = $this->request_api( $api_url, $api_params, 'live' );
287 519 if ( isset( $api_response->error ) ) {
288 520 return $api_response;
289 521 }
290 522
291 - $items = $api_response->items;
292 - if ( ! is_array( $items ) || count( $items ) == 0 ) {
293 - return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
523 + $videos = $this->parse_videos( $api_response );
524 + if ( isset( $videos->error ) ) {
525 + $livestream_settings = get_option( 'ayg_livestream_settings' );
526 + return $this->get_error( '<div class="ayg-livestream-fallback-message">' . $livestream_settings['fallback_message'] . '</div>' );
294 527 }
295 528
296 - // Process result
529 + // Process output
297 530 $response = new stdClass();
298 - $response->playlistId = $items[0]->contentDetails->relatedPlaylists->uploads;
531 + $response->videos = $videos;
299 532
300 - return $response;
301 -
533 + return $response;
302 534 }
303 535
304 536 /**
305 - * Get videos using search API.
537 + * Get details of the given video ID.
306 538 *
307 539 * @since 1.0.0
308 540 * @access private
309 541 * @param array $params Array of query params.
@@ -308,34 +540,22 @@
308 540 * @access private
309 541 * @param array $params Array of query params.
310 542 * @return stdClass
311 543 */
312 - private function request_api_search( $params = array() ) {
313 -
314 - $api_url = $this->get_api_url( 'search.list' );
315 -
316 - $params['q'] = $params['id'];
317 -
318 - if ( ! empty( $params['q'] ) ) {
319 - $params['q'] = str_replace( '|', '%7C', $params['q'] );
320 - }
321 -
322 - $params['type'] = 'video'; // Overrides user defined type value 'search'
323 -
544 + private function request_api_video( $params = array() ) {
545 + $api_url = $this->get_api_url( 'videos.list' );
546 +
547 + $params['id'] = $this->parse_youtube_id_from_url( $params['src'], 'video' );
548 +
324 549 $api_params = $this->safe_merge_params(
325 550 array(
326 - 'q' => '',
327 - 'channelId' => '',
328 - 'type' => 'video',
329 - 'part' => 'id,snippet',
330 - 'order' => 'date',
331 - 'maxResults' => 50,
332 - 'pageToken' => '',
333 - 'cache' => 0
334 - ),
551 + 'id' => '',
552 + 'part' => 'id,snippet,contentDetails,status',
553 + 'cache' => 0
554 + ),
335 555 $params
336 - );
337 -
556 + );
557 +
338 558 $api_response = $this->request_api( $api_url, $api_params );
339 559 if ( isset( $api_response->error ) ) {
340 560 return $api_response;
341 561 }
@@ -344,16 +564,14 @@
344 564 if ( isset( $videos->error ) ) {
345 565 return $videos;
346 566 }
347 567
348 - // Process result
568 + // Process output
349 569 $response = new stdClass();
350 - $response->page_info = $this->parse_page_info( $api_response );
351 570 $response->videos = $videos;
352 571
353 - return $response;
354 -
355 - }
572 + return $response;
573 + }
356 574
357 575 /**
358 576 * Get details of the given video IDs.
359 577 *
@@ -362,13 +580,12 @@
362 580 * @param array $params Array of query params.
363 581 * @return stdClass
364 582 */
365 583 private function request_api_videos( $params = array() ) {
366 -
367 584 $api_url = $this->get_api_url( 'videos.list' );
368 585
369 - $urls = str_replace( "\n\r", ',', $params['id'] );
370 - $urls = str_replace( ' ', ',', $params['id'] );
586 + $urls = str_replace( "\n\r", ',', $params['src'] );
587 + $urls = str_replace( ' ', ',', $urls );
371 588 $urls = explode( ',', $urls );
372 589 $urls = array_filter( $urls );
373 590
374 591 $all_ids = array();
@@ -381,12 +598,9 @@
381 598 $current_page = isset( $params['pageToken'] ) ? (int) $params['pageToken'] : 1;
382 599 $current_page = max( $current_page, 1 );
383 600 $current_page = min( $current_page, $total_pages );
384 601
385 - $offset = ( $current_page - 1 ) * $params['maxResults'];
386 - if ( $offset < 0 ) {
387 - $offset = 0;
388 - }
602 + $offset = max( 0, ( $current_page - 1 ) * $params['maxResults'] );
389 603
390 604 $current_ids = array_slice( $all_ids, $offset, $params['maxResults'] );
391 605 $params['id'] = implode( ',', $current_ids );
392 606
@@ -392,9 +606,9 @@
392 606
393 607 $api_params = $this->safe_merge_params(
394 608 array(
395 609 'id' => '',
396 - 'part' => 'id,snippet,contentDetails,statistics,status',
610 + 'part' => 'id,snippet,contentDetails,status',
397 611 'cache' => 0
398 612 ),
399 613 $params
400 614 );
@@ -404,15 +618,20 @@
404 618 return $api_response;
405 619 }
406 620
407 621 $videos = $this->parse_videos( $api_response );
622 + if ( isset( $videos->error ) ) {
623 + return $videos;
624 + }
408 625
409 - // Process result
626 + // Process output
410 627 $response = new stdClass();
411 628 $response->videos = $videos;
412 629
413 630 $response->page_info = array(
414 - 'videos_found' => $total_videos
631 + 'videos_found' => $total_videos,
632 + 'total_pages' => $total_pages,
633 + 'paged' => $current_page
415 634 );
416 635
417 636 if ( $current_page > 1 ) {
418 637 $response->page_info['prev_page_token'] = $current_page - 1;
@@ -421,48 +640,97 @@
421 640 if ( $current_page < $total_pages ) {
422 641 $response->page_info['next_page_token'] = $current_page + 1;
423 642 }
424 643
425 - return $response;
426 -
644 + return $response;
427 645 }
428 646
429 647 /**
430 - * Get details of the given video ID.
648 + * Get videos from our custom database table "{$wpdb->prefix}ayg_videos".
431 649 *
432 - * @since 1.0.0
650 + * @since 2.5.7
433 651 * @access private
434 652 * @param array $params Array of query params.
435 653 * @return stdClass
436 654 */
437 - private function request_api_video( $params = array() ) {
655 + private function get_videos_from_db( $params = array() ) {
656 + global $wpdb;
438 657
439 - $api_url = $this->get_api_url( 'videos.list' );
658 + $videos_table = $wpdb->prefix . 'ayg_videos';
659 + $galleries_table = $wpdb->prefix . 'ayg_galleries';
660 +
661 + $search_term = '%' . $wpdb->esc_like( $params['searchTerm'] ) . '%';
662 + $gallery_id = $params['uid'];
663 +
664 + // Get Total Videos Count
665 + $total_query = $wpdb->prepare(
666 + "SELECT COUNT(*)
667 + FROM $videos_table AS v
668 + INNER JOIN $galleries_table AS g ON v.id = g.video_id
669 + WHERE g.gallery_id = %s
670 + AND (v.title LIKE %s OR v.description LIKE %s)",
671 + $gallery_id, $search_term, $search_term
672 + );
440 673
441 - $params['id'] = $this->parse_youtube_id_from_url( $params['id'], 'video' );
674 + $total_videos = $wpdb->get_var( $total_query );
675 +
676 + if ( empty( $total_videos ) ) {
677 + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
678 + }
679 +
680 + // Fetch Paginated Videos
681 + $limit = $params['maxResults'];
442 682
443 - $api_params = $this->safe_merge_params(
444 - array(
445 - 'id' => '',
446 - 'part' => 'id,snippet,contentDetails,statistics,status',
447 - 'cache' => 0
448 - ),
449 - $params
683 + $total_pages = ceil( $total_videos / $limit );
684 +
685 + $current_page = isset( $params['pageToken'] ) ? (int) $params['pageToken'] : 1;
686 + $current_page = max( $current_page, 1 );
687 + $current_page = min( $current_page, $total_pages );
688 +
689 + $offset = max( 0, ( $current_page - 1 ) * $limit );
690 +
691 + $query = $wpdb->prepare(
692 + "SELECT v.*
693 + FROM $videos_table AS v
694 + INNER JOIN $galleries_table AS g ON v.id = g.video_id
695 + WHERE g.gallery_id = %s
696 + AND (v.title LIKE %s OR v.description LIKE %s)
697 + ORDER BY v.published_at_datetime DESC
698 + LIMIT %d OFFSET %d",
699 + $gallery_id, $search_term, $search_term, $limit, $offset
450 700 );
701 +
702 + $videos = $wpdb->get_results( $query );
451 703
452 - $api_response = $this->request_api( $api_url, $api_params );
453 - if ( isset( $api_response->error ) ) {
454 - return $api_response;
704 + if ( empty( $videos ) ) {
705 + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
455 706 }
456 707
457 - $videos = $this->parse_videos( $api_response );
708 + foreach ( $videos as $index => $video ) {
709 + if ( ! empty( $video->thumbnails ) ) {
710 + $videos[ $index ]->thumbnails = maybe_unserialize( $video->thumbnails );
711 + }
712 + }
458 713
459 - // Process result
714 + // Process output
460 715 $response = new stdClass();
461 716 $response->videos = $videos;
462 717
463 - return $response;
464 -
718 + $response->page_info = array(
719 + 'videos_found' => $total_videos,
720 + 'total_pages' => $total_pages,
721 + 'paged' => $current_page
722 + );
723 +
724 + if ( $current_page > 1 ) {
725 + $response->page_info['prev_page_token'] = $current_page - 1;
726 + }
727 +
728 + if ( $current_page < $total_pages ) {
729 + $response->page_info['next_page_token'] = $current_page + 1;
730 + }
731 +
732 + return $response;
465 733 }
466 734
467 735 /**
468 736 * Get API URL by request.
@@ -480,47 +748,65 @@
480 748 * Request data from the API server.
481 749 *
482 750 * @since 1.0.0
483 751 * @access private
484 - * @param string $url YouTube API URL.
485 - * @param array $params Array of query params.
752 + * @param string $url YouTube API URL.
753 + * @param array $params Array of query params.
754 + * @param string $context "channel_id", "playlist_id", "videos", or "live"
486 755 * @return mixed
487 756 */
488 - private function request_api( $url, $params ) {
489 -
757 + private function request_api( $url, $params, $context = 'videos' ) {
490 758 $params['key'] = $this->api_key;
759 +
760 + // Build API URL
761 + $cache_duration = 0;
762 + if ( isset( $params['cache'] ) ) {
763 + $cache_duration = (int) $params['cache'];
764 + unset( $params['cache'] );
765 + }
766 + $cache_duration = min( $cache_duration, 2419200 ); // Max cache duration: 1 Month
491 767
492 - // Request data from transients
493 - $transient_url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params );
494 - $transient_key = 'ayg_' . md5( $transient_url );
495 - $transient_data = get_transient( $transient_key );
768 + $q = '';
769 + if ( isset( $params['q'] ) ) {
770 + $q = $params['q'];
771 + unset( $params['q'] );
772 + }
496 773
497 - if ( ! empty( $transient_data ) ) {
498 - return $transient_data;
774 + $api_url = $url . ( strpos( $url, '?' ) === false ? '?' : '&' ) . http_build_query( $params );
775 + if ( ! empty( $q ) ) {
776 + $api_url .= '&q=' . $q;
499 777 }
500 778
501 - $transient_expiration = 0;
502 -
503 - if ( isset( $params['cache'] ) ) {
504 - $transient_expiration = (int) $params['cache'];
505 - unset( $params['cache'] );
779 + // Request from cache
780 + if ( ! $this->is_development_mode && $cache_duration > 0 ) {
781 + $cache_key = 'ayg_' . md5( $api_url );
782 + $cache_data = get_transient( $cache_key );
783 +
784 + if ( ! empty( $cache_data ) ) {
785 + return $cache_data;
786 + }
506 787 }
507 788
508 - // Request data from API server
509 - $url = $url . ( strpos( $url, '?' ) === false ? '?' : '' ) . http_build_query( $params );
789 + // Request from API
790 + $timeout = apply_filters( 'ayg_api_request_timeout', 15 );
791 +
792 + $request = wp_remote_get( $api_url, array(
793 + 'headers' => [ 'referer' => home_url() ],
794 + 'timeout' => $timeout, // Increase timeout if needed
795 + ) );
510 796
511 - $request = wp_remote_get( $url );
512 -
513 797 if ( is_wp_error( $request ) ) {
514 798 return $this->get_error( $request->get_error_message() );
515 799 }
516 800
517 801 $body = wp_remote_retrieve_body( $request );
802 + $data = json_decode( $body );
518 803
519 - $data = json_decode( $body );
804 + if ( empty( $data ) ) {
805 + return $this->get_error( __( 'Empty or invalid API response', 'automatic-youtube-gallery' ) );
806 + }
520 807
521 808 if ( isset( $data->error ) ) {
522 -
523 809 $message = "Error " . $data->error->code . " " . $data->error->message;
524 810
525 811 if ( isset( $data->error->errors[0] ) ) {
526 812 $message .= " : " . $data->error->errors[0]->reason;
@@ -525,33 +811,49 @@
525 811 if ( isset( $data->error->errors[0] ) ) {
526 812 $message .= " : " . $data->error->errors[0]->reason;
527 813 }
528 814
529 - return $this->get_error( $message );
530 -
815 + return $this->get_error( $message );
531 816 }
532 817
533 - // Store data in transients
534 - if ( $transient_expiration > 0 ) {
535 -
536 - set_transient( $transient_key, $data, $transient_expiration );
818 + // Store in cache (transients)
819 + $cache_enabled = false;
537 820
821 + if ( ! $this->is_development_mode && $cache_duration > 0 ) {
822 + if ( 'videos' === $context ) {
823 + if ( ! empty( $data->items ) && is_array( $data->items ) ) {
824 + $cache_enabled = true;
825 + }
826 + }
827 +
828 + if ( 'live' === $context ) {
829 + $cache_enabled = true;
830 + }
831 + }
832 +
833 + if ( $cache_enabled ) {
834 + set_transient( $cache_key, $data, $cache_duration );
835 +
538 836 // Get the current list of transients
539 - $transient_keys = get_option( 'ayg_transient_keys', array() );
837 + $cache_keys = get_option( 'ayg_transient_keys', array() );
838 + if ( ! is_array( $cache_keys ) ) {
839 + $cache_keys = (array) $cache_keys;
840 + }
540 841
541 842 // Append our new one
542 - if ( ! in_array( $transient_key, $transient_keys ) ) {
543 - $transient_keys[] = $transient_key;
843 + if ( ! in_array( $cache_key, $cache_keys ) ) {
844 + $cache_keys[] = $cache_key;
544 845 }
545 846
546 847 // Save it to the DB
547 - update_option( 'ayg_transient_keys', $transient_keys );
548 -
848 + update_option( 'ayg_transient_keys', $cache_keys );
549 849 }
550 850
851 + // Store videos in our custom database table "{$wpdb->prefix}ayg_videos"
852 + ayg_db_store_videos( $data, $this->params );
853 +
551 854 // Finally return the data
552 855 return $data;
553 -
554 856 }
555 857
556 858 /**
557 859 * Parse videos from the YouTube API response object.
@@ -561,19 +863,16 @@
561 863 * @param object $data YouTube API response object.
562 864 * @return mixed
563 865 */
564 866 private function parse_videos( $data ) {
565 -
566 - $items = $data->items;
567 -
568 - if ( ! is_array( $items ) || 0 == count( $items ) ) {
867 + if ( empty( $data->items ) || ! is_array( $data->items ) ) {
569 868 return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
570 869 }
571 870
871 + $items = $data->items;
572 872 $videos = array();
573 873
574 874 foreach ( $items as $item ) {
575 -
576 875 $video = new stdClass();
577 876
578 877 // Video ID
579 878 $video->id = '';
@@ -585,10 +884,17 @@
585 884 } elseif ( isset( $item->id ) && isset( $item->id->videoId ) ) {
586 885 $video->id = $item->id->videoId;
587 886 } elseif ( isset( $item->id ) ) {
588 887 $video->id = $item->id;
589 - }
888 + }
590 889
890 + // Video channel ID
891 + $video->channel_id = '';
892 +
893 + if ( isset( $item->snippet->channelId ) ) {
894 + $video->channel_id = $item->snippet->channelId;
895 + }
896 +
591 897 // Video title
592 898 $video->title = $item->snippet->title;
593 899
594 900 // Video description
@@ -601,24 +907,33 @@
601 907
602 908 // Video publish date
603 909 $video->published_at = $item->snippet->publishedAt;
604 910
605 - // Is video private?
606 - $video->is_private = 0;
911 + // Push resulting object to the main array
912 + $status = 'private';
913 +
914 + if ( isset( $item->status ) && ( 'public' == $item->status->privacyStatus || 'unlisted' == $item->status->privacyStatus ) ) {
915 + $status = 'public';
916 + }
607 917
608 - if ( ! isset( $item->snippet->thumbnails ) || ( isset( $item->snippet->status ) && 'private' == $item->snippet->status->privacyStatus ) ) {
609 - $video->is_private = 1;
918 + if ( isset( $item->snippet->status ) && ( 'public' == $item->snippet->status->privacyStatus || 'unlisted' == $item->snippet->status->privacyStatus ) ) {
919 + $status = 'public';
610 920 }
611 921
612 - // Push resulting object to the main array
613 - if ( ! $video->is_private ) {
922 + if ( 'youtube#searchResult' == $item->kind ) {
923 + $status = 'public';
924 + }
925 +
926 + if ( 'public' == $status ) {
614 927 $videos[] = $video;
615 - }
928 + }
929 + }
616 930
931 + if ( 0 == count( $videos ) ) {
932 + return $this->get_error( __( 'No videos found matching your query.', 'automatic-youtube-gallery' ) );
617 933 }
618 934
619 - return $videos;
620 -
935 + return $videos;
621 936 }
622 937
623 938 /**
624 939 * Parse page info from the YouTube API response object.
@@ -628,15 +943,26 @@
628 943 * @param object $data YouTube API response object.
629 944 * @return array
630 945 */
631 946 private function parse_page_info( $data ) {
947 + $page_info = array(
948 + 'videos_found' => 0
949 + );
632 950
633 - $page_info = array();
634 -
635 - // Total count of videos found
951 + // Total number of videos found
636 952 if ( isset( $data->pageInfo ) && isset( $data->pageInfo->totalResults ) ) {
637 - $page_info['videos_found'] = $data->pageInfo->totalResults;
638 - }
953 + $page_info['videos_found'] = (int) $data->pageInfo->totalResults;
954 + }
955 +
956 + // Calculate total number of pages
957 + if ( $page_info['videos_found'] > 0 ) {
958 + if ( 'search' == $this->params['type'] ) {
959 + $limit = min( (int) $this->params['limit'], $page_info['videos_found'] );
960 + $page_info['total_pages'] = ceil( $limit / (int) $this->params['maxResults'] );
961 + } else {
962 + $page_info['total_pages'] = ceil( $page_info['videos_found'] / (int) $this->params['maxResults'] );
963 + }
964 + }
639 965
640 966 // Token for the previous page
641 967 if ( isset( $data->prevPageToken ) ) {
642 968 $page_info['prev_page_token'] = $data->prevPageToken;
@@ -647,9 +973,8 @@
647 973 $page_info['next_page_token'] = $data->nextPageToken;
648 974 }
649 975
650 976 return $page_info;
651 -
652 977 }
653 978
654 979 /**
655 980 * Combine user params with known params and fill in defaults when needed.
@@ -660,14 +985,12 @@
660 985 * @param array $params User defined params.
661 986 * @return array $out Combined and filtered params array.
662 987 */
663 988 private function safe_merge_params( $pairs, $params ) {
664 -
665 989 $params = (array) $params;
666 990 $out = array();
667 991
668 992 foreach ( $pairs as $name => $default ) {
669 -
670 993 if ( array_key_exists( $name, $params ) ) {
671 994 $out[ $name ] = $params[ $name ];
672 995 } else {
673 996 $out[ $name ] = $default;
@@ -675,13 +998,11 @@
675 998
676 999 if ( empty( $out[ $name ] ) ) {
677 1000 unset( $out[ $name ] );
678 1001 }
679 -
680 1002 }
681 1003
682 1004 return $out;
683 -
684 1005 }
685 1006
686 1007 /**
687 1008 * Build error object.
@@ -691,14 +1012,12 @@
691 1012 * @param string $message Error message.
692 1013 * @return object Error object.
693 1014 */
694 1015 private function get_error( $message ) {
695 -
696 1016 $obj = new stdClass();
697 1017 $obj->error = 1;
698 1018 $obj->error_message = $message;
699 1019
700 1020 return $obj;
701 -
702 1021 }
703 1022
704 1023 }