PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 3.7.3
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v3.7.3
4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / EmbedPress / Providers / Youtube.php
embedpress / EmbedPress / Providers Last commit date
Boomplay.php 5 years ago Giphy.php 5 years ago GitHub.php 3 years ago GoogleDocs.php 5 years ago GoogleDrive.php 3 years ago GoogleMaps.php 3 years ago NRKRadio.php 3 years ago OpenSea.php 3 years ago Twitch.php 5 years ago Youtube.php 3 years ago index.html 7 years ago
Youtube.php
935 lines
1 <?php
2
3 /**
4 * Youtube.php
5 *
6 * @package Embera
7 * @author Michael Pratt <yo@michael-pratt.com>
8 * @link http://www.michael-pratt.com/
9 *
10 * For the full copyright and license information, please view the LICENSE
11 * file that was distributed with this source code.
12 */
13
14 namespace EmbedPress\Providers;
15
16 use Embera\Provider\ProviderAdapter;
17 use Embera\Provider\ProviderInterface;
18 use Embera\Url;
19
20 /**
21 * youtube.com Provider
22 * @link https://youtube.com
23 * @link https://youtube-eng.googleblog.com/2009/10/oembed-support_9.html
24 */
25
26
27 class Youtube extends ProviderAdapter implements ProviderInterface {
28 /** inline {@inheritdoc} */
29 protected $shouldSendRequest = false;
30 public static $curltimeout = 30;
31 /** inline {@inheritdoc} */
32 protected $endpoint = 'https://www.youtube.com/oembed?format=json&scheme=https';
33 protected static $channel_endpoint = 'https://www.googleapis.com/youtube/v3/';
34 /** @var array Array with allowed params for the current Provider */
35 protected $allowedParams = [ 'maxwidth', 'maxheight', 'pagesize', 'thumbnail', 'gallery', 'hideprivate', 'columns', 'ispagination', 'gapbetweenvideos' ];
36
37 /** inline {@inheritdoc} */
38 protected static $hosts = [
39 'm.youtube.com', 'youtube.com', 'youtu.be',
40 ];
41
42 /** inline {@inheritdoc} */
43 protected $httpsSupport = true;
44
45 public function getAllowedParams(){
46 return $this->allowedParams;
47 }
48
49 /** inline {@inheritdoc} */
50 public function validateUrl(Url $url) {
51 return (bool) (preg_match('~\/channel\/|\/c\/|\/user\/|\/@\w+|(?:https?:\/\/)?(?:www\.)?(?:youtube.com\/)(\w+)[^?\/]*$~i', (string) $url));
52 }
53
54 public function validateTYLiveUrl($url) {
55 return (bool) (preg_match('~(?:https?:\/\/)?(?:www\.)?(?:youtube.com\/(?:channel|c|user)\/\w+\/live|@\w+\/live)~i', (string) $url));
56 }
57
58 /** inline {@inheritdoc} */
59 public function normalizeUrl(Url $url) {
60 return $url;
61 }
62
63 public function isChannel($url = null) {
64 if (empty($url)) {
65 $url = $this->url;
66 }
67 $channel = $this->getChannel($url);
68 return !empty($channel['id']);
69 }
70
71 public function getChannel($url = null) {
72 if (empty($url)) {
73 $url = $this->url;
74 }
75 preg_match('~\/(channel|c|user)\/(.+)~i', (string) $url, $matches);
76 if(empty($matches[1])){
77 preg_match('~(?:https?:\/\/)?(?:www\.)?(?:youtube.com\/)(\w+)[^?\/]*$~i', (string) $url, $matches);
78 if(!empty($matches[1])){
79 return [
80 "type" => 'user',
81 "id" => $matches[1],
82 ];
83 }
84 }
85 if(empty($matches[1])){
86 preg_match('~\/(@)(\w+)~i', (string) $url, $matches);
87 if(!empty($matches[1])){
88 if(!empty($this->get_youtube_handler($this->url))){
89 if(!empty($this->get_channel_id_by_handler($this->get_youtube_handler($this->url)))){
90 $channelId = $this->get_channel_id_by_handler($this->get_youtube_handler($this->url));
91 }
92 }
93 return [
94 "type" => 'user',
95 "id" => $channelId,
96 ];
97 }
98 }
99 return [
100 "type" => isset($matches[1]) ? $matches[1] : '',
101 "id" => isset($matches[2]) ? $matches[2] : '',
102 ];
103 }
104
105 /** inline {@inheritdoc} */
106 public function getEndpoint() {
107 if ($this->isChannel()) {
108 $apiEndpoint = 'https://www.googleapis.com/youtube/v3/channels';
109 return $apiEndpoint;
110 }
111 return (string) $this->endpoint;
112 }
113
114 protected static function get_api_key() {
115 $settings = (array) get_option(EMBEDPRESS_PLG_NAME . ':youtube', []);
116 return !empty($settings['api_key']) ? $settings['api_key'] : '';
117 }
118
119 protected static function get_pagesize() {
120 $settings = (array) get_option(EMBEDPRESS_PLG_NAME . ':youtube', []);
121 return !empty($settings['pagesize']) ? $settings['pagesize'] : '';
122 }
123
124 /** inline {@inheritdoc} */
125 public function getParams() {
126 $params = parent::getParams();
127 if ($this->isChannel() && self::get_api_key()) {
128 $channel = $this->getChannel();
129 $params['part'] = 'contentDetails,snippet';
130 $params['key'] = self::get_api_key();
131 if ($channel['type'] == 'c') {
132 $params['forUsername'] = $channel['id'];
133 } else {
134 $params['id'] = $channel['id'];
135 }
136 unset($params['url']);
137 }
138 return $params;
139 }
140
141 /**
142 * Builds a valid Oembed query string based on the given parameters,
143 * Since this method uses the http_build_query function, there is no
144 * need to pass urlencoded parameters, http_build_query already does
145 * this for us.
146 *
147 * @param string $endpoint The Url to the Oembed endpoint
148 * @param array $params Parameters for the query string
149 * @return string
150 */
151
152 protected function constructUrl($endpoint, array $params = array())
153 {
154 $endpoint = self::$channel_endpoint . $endpoint;
155
156 return $endpoint . ((strpos($endpoint, '?') === false) ? '?' : '&') . http_build_query(array_filter($params));
157 }
158
159 public function getStaticResponse() {
160 $results = [
161 "title" => "",
162 "type" => "video",
163 'provider_name' => $this->getProviderName(),
164 "provider_url" => "https://www.youtube.com/",
165 'html' => '',
166 ];
167
168 $params = $this->getParams();
169
170 if (preg_match("/^https?:\/\/(?:www\.)?youtube\.com\/channel\/([\w-]+)\/live$/", $this->url, $matches) || $this->validateTYLiveUrl($this->url)) {
171
172 if(!empty($matches[1])){
173 $channelId = $matches[1];
174 }
175
176 if(!empty($this->get_youtube_handler($this->url))){
177 if(!empty($this->get_channel_id_by_handler($this->get_youtube_handler($this->url)))){
178 $channelId = $this->get_channel_id_by_handler($this->get_youtube_handler($this->url));
179 }
180 }
181
182 $embedUrl = 'https://www.youtube.com/embed/live_stream?channel='.$channelId.'&feature=oembed';
183
184 $attr = [];
185 $attr[] = 'width="'.$params['maxheight'].'"';
186 $attr[] = 'height="'.$params['maxheight'].'";';
187 $attr[] = 'src="' . $embedUrl . '"';
188 $attr[] = 'frameborder="0"';
189 $attr[] = 'allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"';
190 $attr[] = 'allowfullscreen';
191
192 $results['html'] = '<iframe ' . implode(' ', $attr) . '></iframe>';
193 }
194 else if($this->isChannel()){
195 $channel = $this->getChannelGallery();
196 $results = array_merge($results, $channel);
197 }
198 return $results;
199 }
200
201 public function getChannelPlaylist(){
202 $result = [
203 "playlistID" => '',
204 "title" => '',
205 ];
206 $channel = $this->getChannel();
207 $channel_url = $this->constructUrl('channels', $this->getParams());
208 $transient_key = 'ep_embed_youtube_channel_playlist_id_' . md5($channel_url);
209 $jsonResult = get_transient($transient_key);
210
211 if(!empty($jsonResult)){
212 return $jsonResult;
213 }
214
215 if($channel['type'] == 'user' || $channel['type'] == 'c'){
216 $this->getChannelIDbyUsername();
217 $channel_url = $this->constructUrl('channels', $this->getParams());
218 }
219
220 if (empty(self::get_api_key())) {
221 $result['error'] = true;
222 $result['html'] = self::get_api_key_error_message();
223 return $result;
224 }
225
226 $apiResult = wp_remote_get($channel_url, array('timeout' => self::$curltimeout));
227 if (is_wp_error($apiResult)) {
228 $result['error'] = true;
229 $result['html'] = self::clean_api_error_html($apiResult->get_error_message(), true);
230 set_transient($transient_key, $result, 10);
231 return $result;
232 }
233 $jsonResult = json_decode($apiResult['body']);
234
235
236 if (isset($jsonResult->error)) {
237 $result['error'] = true;
238 if (isset($jsonResult->error->message)) {
239 $result['html'] = self::clean_api_error_html($jsonResult->error->message, true);
240 }
241 else{
242 $result['html'] = self::clean_api_error_html(__('Sorry, there may be an issue with your YouTube API key.', 'embedpress'));
243 }
244 set_transient($transient_key, $result, MINUTE_IN_SECONDS);
245 return $result;
246 }
247 elseif(!empty($jsonResult->items[0]->contentDetails->relatedPlaylists->uploads)){
248 $result['playlistID'] = $jsonResult->items[0]->contentDetails->relatedPlaylists->uploads;
249 $result['title'] = isset($jsonResult->items[0]->snippet->title) ? $jsonResult->items[0]->snippet->title : '';
250 set_transient($transient_key, $result, DAY_IN_SECONDS);
251 }
252
253 return $result;
254 }
255
256 public function get_youtube_handler($url){
257 // preg_match('/^https:\/\/www.youtube.com\/@(.+)\/live$/i', $url, $matches);
258 preg_match('/^https:\/\/www.youtube.com\/@([^\/?]+)/i', $url, $matches);
259
260
261 $handle_name = '';
262 if(!empty($matches[1])){
263 $handle_name = $matches[1];
264 }
265
266 return $handle_name;
267 }
268
269 public function getChannelIDbyUsername(){
270 $url = $this->getUrl();
271 $apiResult = wp_remote_get($url, array('timeout' => self::$curltimeout));
272
273 if (!is_wp_error($apiResult)) {
274 $channel_html = $apiResult['body'];
275 preg_match("/<meta\s+itemprop=[\"']channelId[\"']\s+content=[\"'](.*?)[\"']\/?>/", $channel_html, $matches);
276 if(!empty($matches[1])){
277 $url = "https://www.youtube.com/channel/{$matches[1]}";
278 $this->url = $this->normalizeUrl(new Url($url));
279 }
280 }
281 }
282
283 public function get_channel_id_by_handler($handler){
284 $api_key = self::get_api_key();
285 $username = $handler;
286
287 // Check if the transient exists
288 $transient_name = 'channel_id_' . $handler;
289 $channel_id = get_transient( $transient_name );
290 if ( $channel_id ) {
291 // If the transient exists, return the channel ID
292 return $channel_id;
293 }
294
295 $url = "https://www.googleapis.com/youtube/v3/search?part=id&type=channel&q={$username}&key={$api_key}";
296
297 $response = wp_remote_get($url);
298 if ( is_wp_error( $response ) ) {
299 return false;
300 }
301
302 $json_response = json_decode(wp_remote_retrieve_body($response), true);
303
304 if(isset($json_response['error'])) {
305 return false;
306 } else {
307 $channel_id = $json_response['items'][0]['id']['channelId'];
308
309 // Set the transient for 1 day (86400 seconds)
310 set_transient( $transient_name, $channel_id, MONTH_IN_SECONDS );
311
312 return $channel_id;
313 }
314 }
315
316 /** inline {@inheritdoc} */
317 public function getChannelGallery() {
318 $response = [];
319 $channel = $this->getChannelPlaylist();
320 if(!empty($channel['error'])){
321 return $channel;
322 }
323 if (!empty($channel["playlistID"])) {
324 $params = $this->getParams();
325 $the_playlist_id = $channel["playlistID"];
326 $rel = 'https://www.youtube.com/embed?listType=playlist&list=' . esc_attr($the_playlist_id);
327 $title = $channel['title'];
328 $main_iframe = "";
329 $gallery_args = [
330 'playlistId' => $the_playlist_id,
331 ];
332 if(!empty($params['pagesize'])){
333 $gallery_args['pagesize'] = $params['pagesize'];
334 }
335 $gallery = self::get_gallery_page($gallery_args);
336
337 if (!empty($gallery->first_vid)) {
338 $rel = "https://www.youtube.com/embed/{$gallery->first_vid}?feature=oembed";
339 $main_iframe = "<div class='ep-first-video'><iframe width='{$params['maxwidth']}' height='{$params['maxheight']}' src='$rel' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen title='{$title}'></iframe></div>";
340 }
341 if($gallery->html && $this->validateTYLiveUrl($this->getUrl())){
342 $styles = self::styles($params, $this->getUrl());
343 return [
344 "title" => $title,
345 "html" => "<div class='ep-player-wrap'>$main_iframe $styles</div>",
346 ];
347 }
348 if($gallery->html){
349 $styles = self::styles($params, $this->getUrl());
350 return [
351 "title" => $title,
352 "html" => "<div class='ep-player-wrap'>$main_iframe {$gallery->html} $styles</div>",
353 ];
354 }
355 }
356 elseif ($this->isChannel() && empty(self::get_api_key()) && current_user_can('manage_options')) {
357 return [
358 "html" => "<div class='ep-player-wrap'>" . __('Please enter your YouTube API key to embed YouTube Channel.', 'embedpress') . "</div>",
359 ];
360 }
361
362 return $response;
363 }
364
365 /**
366 * Undocumented function
367 *
368 * @param array $options
369 * @return object
370 */
371 public static function get_gallery_page($options) {
372 $nextPageToken = '';
373 $prevPageToken = '';
374 $gallobj = new \stdClass();
375 $options = wp_parse_args($options, [
376 'playlistId' => '',
377 'pageToken' => '',
378 'pagesize' => self::get_pagesize() ? self::get_pagesize() : 6,
379 'currentpage' => '',
380 'columns' => 3,
381 'thumbnail' => 'medium',
382 'gallery' => true,
383 'autonext' => true,
384 'thumbplay' => true,
385 'apiKey' => self::get_api_key(),
386 'hideprivate' => '',
387 ]);
388 $options['pagesize'] = $options['pagesize'] > 50 ? 50 : $options['pagesize'];
389 $options['pagesize'] = $options['pagesize'] < 1 ? 1 : $options['pagesize'];
390
391 if (empty($options['apiKey'])) {
392 $gallobj->html = self::get_api_key_error_message();
393 return $gallobj;
394 }
395
396 $apiEndpoint = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,status&playlistId=' . $options['playlistId']
397 . '&maxResults=' . $options['pagesize']
398 . '&key=' . $options['apiKey'];
399 if ($options['pageToken'] != null) {
400 $apiEndpoint .= '&pageToken=' . $options['pageToken'];
401 }
402
403 $transient_key = 'ep_embed_youtube_channel_' . md5($apiEndpoint);
404 $gallobj->transient_key = $transient_key;
405 $jsonResult = get_transient($transient_key);
406 if (empty($jsonResult)) {
407 $apiResult = wp_remote_get($apiEndpoint, array('timeout' => self::$curltimeout));
408 if (is_wp_error($apiResult)) {
409 $gallobj->html = self::clean_api_error_html($apiResult->get_error_message(), true);
410 return $gallobj;
411 }
412 $jsonResult = json_decode($apiResult['body']);
413 if (empty($jsonResult->error)) {
414 set_transient($transient_key, $jsonResult, MINUTE_IN_SECONDS * 20);
415 }
416 else{
417 set_transient($transient_key, $jsonResult, 10);
418 }
419 }
420
421
422 if (isset($jsonResult->error)) {
423 if(!empty($jsonResult->error->errors[0]->reason) && $jsonResult->error->errors[0]->reason == 'playlistNotFound'){
424 $gallobj->html = self::clean_api_error_html(__('There is nothing on the playlist.', 'embedpress'));
425 return $gallobj;
426 }
427 if (isset($jsonResult->error->message)) {
428 $gallobj->html = self::clean_api_error_html($jsonResult->error->message);
429 return $gallobj;
430 }
431 $gallobj->html = self::clean_api_error_html(__('Sorry, there may be an issue with your YouTube API key.', 'embedpress'));
432 return $gallobj;
433 }
434
435
436
437 $resultsPerPage = $jsonResult->pageInfo->resultsPerPage;
438 $totalResults = $jsonResult->pageInfo->totalResults;
439 $totalPages = ceil($totalResults / $resultsPerPage);
440 if (isset($jsonResult->nextPageToken)) {
441 $nextPageToken = $jsonResult->nextPageToken;
442 }
443
444 if (isset($jsonResult->prevPageToken)) {
445 $prevPageToken = $jsonResult->prevPageToken;
446 }
447
448
449 if (!empty($jsonResult->items) && is_array($jsonResult->items)) :
450 if($options['gallery'] === "false"){
451 $gallobj->html = "";
452 if(count($jsonResult->items) === 1){
453 $gallobj->first_vid = self::get_id($jsonResult->items[0]);
454 }
455 return $gallobj;
456 }
457
458 if(count($jsonResult->items) === 1 && empty($nextPageToken) && empty($prevPageToken)){
459 $gallobj->first_vid = self::get_id($jsonResult->items[0]);
460 $gallobj->html = "";
461 return $gallobj;
462 }
463
464 if (strpos($options['playlistId'], 'UU') === 0) {
465 // sort only channels
466 usort($jsonResult->items, array(get_class(), 'compare_vid_date')); // sorts in place
467 }
468
469 ob_start();
470 ?>
471 <div class="ep-youtube__content__block" data-unique-id="<?php echo wp_rand(); ?>">
472 <div class="youtube__content__body">
473 <div class="content__wrap">
474 <?php foreach ($jsonResult->items as $item) : ?>
475 <?php
476 $privacyStatus = isset($item->status->privacyStatus) ? $item->status->privacyStatus : null;
477 $thumbnail = self::get_thumbnail_url($item, $options['thumbnail'], $privacyStatus);
478 $vid = self::get_id($item);
479 if (empty($gallobj->first_vid)) {
480 $gallobj->first_vid = $vid;
481 }
482 if ($privacyStatus == 'private' && $options['hideprivate']) {
483 continue;
484 }
485 ?>
486 <div class="item" data-vid="<?php echo $vid; ?>">
487 <div class="thumb" style="background: <?php echo "url({$thumbnail}) no-repeat center"; ?>">
488 <div class="play-icon">
489 <img src="<?php echo EMBEDPRESS_URL_ASSETS . 'images/youtube/youtube-play.png'; ?>" alt="">
490 </div>
491 </div>
492 <div class="body">
493 <p><?php echo $item->snippet->title; ?></p>
494 </div>
495 </div>
496
497 <?php endforeach; ?>
498 <div class="item" style="height: 0"></div>
499 </div>
500
501
502 <?php if ($totalPages > 1) : ?>
503 <div class="ep-youtube__content__pagination <?php echo (empty($prevPageToken) && empty($nextPageToken)) ? ' hide ' : ''; ?>">
504 <div
505 class="ep-prev" <?php echo empty($prevPageToken) ? ' style="display:none" ' : ''; ?>
506 data-playlistid="<?php echo esc_attr($options['playlistId']) ?>"
507 data-pagetoken="<?php echo esc_attr($prevPageToken) ?>"
508 data-pagesize="<?php echo intval($options['pagesize']) ?>"
509 >
510 <span><?php _e("Prev", "embedpress"); ?></span>
511 </div>
512 <div class="is_desktop_device ep-page-numbers <?php echo $totalPages > 1 ? '' : 'hide'; ?>">
513 <?php
514
515 $numOfPages = $totalPages;
516 $renderedEllipses = false;
517
518 $currentPage = !empty($options['currentpage'])?$options['currentpage'] : 1;
519
520 for($i = 1; $i<=$numOfPages; $i++)
521 {
522 //render pages 1 - 3
523 if($i < 4) {
524 //render link
525 $is_current = $i == (int)$currentPage? "active__current_page" : "";
526
527 echo wp_kses_post("<span class='page-number $is_current' data-page='$i'>$i</span>");
528
529 }
530
531 //render current page number
532 else if($i == (int)$currentPage) {
533 //render link
534 echo wp_kses_post('<span class="page-number active__current_page" data-page="'.$i.'">'.$i.'</span>');
535 //reset ellipses
536 $renderedEllipses = false;
537 }
538
539 //last page number
540 else if ($i >= $numOfPages - 1) {
541 //render link
542 echo wp_kses_post('<span class="page-number" data-page="'.$i.'">'.$i.'</span>');
543 }
544
545 //make sure you only do this once per ellipses group
546 else {
547 if (!$renderedEllipses){
548 print("...");
549 $renderedEllipses = true;
550 }
551 }
552 }
553 ?>
554
555 </div>
556
557 <div class="is_mobile_device ep-page-numbers <?php echo $totalPages > 1 ? '' : 'hide'; ?>">
558 <?php
559
560 $numOfPages = $totalPages;
561 $renderedEllipses = false;
562
563 $currentPage = !empty($options['currentpage'])?$options['currentpage'] : 1;
564
565 for($i = 1; $i<=$numOfPages; $i++)
566 {
567
568 //render current page number
569 if($i == (int)$currentPage) {
570 //render link
571 echo wp_kses_post('<span class="page-number-mobile" data-page="'.$i.'">'.$i.'</span>');
572 //reset ellipses
573 $renderedEllipses = false;
574 }
575
576 //last page number
577 else if ($i >= $numOfPages ) {
578 //render link
579 echo wp_kses_post('...<span class="page-number-mobile" data-page="'.$i.'">'.$i.'</span>');
580 }
581 }
582 ?>
583
584 </div>
585
586
587 <div
588 class="ep-next " <?php echo empty($nextPageToken) ? ' style="display:none" ' : ''; ?>
589 data-playlistid="<?php echo esc_attr($options['playlistId']) ?>"
590 data-pagetoken="<?php echo esc_attr($nextPageToken) ?>"
591 data-pagesize="<?php echo intval($options['pagesize']) ?>"
592 >
593 <span><?php _e("Next ", "embedpress"); ?> </span>
594 </div>
595 </div>
596 <?php endif; ?>
597
598 <div class="ep-loader-wrap">
599 <div class="ep-loader"><img alt="loading" src="<?php echo EMBEDPRESS_URL_ASSETS . 'images/youtube/spin.gif'; ?>"></div>
600 </div>
601
602 </div>
603 </div>
604 <?php
605 $gallobj->html = ob_get_clean();
606 else:
607 $gallobj->html = self::clean_api_error_html(__("There is nothing on the playlist.", 'embedpress'));
608 endif;
609
610 return $gallobj;
611 }
612
613 public static function get_api_key_error_message(){
614 return '<div>' . sprintf(__("EmbedPress: Please enter your YouTube API key at <a class='ep-link' href='%s' target='_blank' style='color: #5b4e96; text-decoration: none'>EmbedPress > Platforms > YouTube</a> to embed YouTube Channel.", "embedpress"), admin_url('?page=embedpress&page_type=youtube#api_key')) . '</div>';
615 }
616
617 public static function get_id($item){
618 $vid = isset($item->snippet->resourceId->videoId) ? $item->snippet->resourceId->videoId : null;
619 $vid = $vid ? $vid : (isset($item->id->videoId) ? $item->id->videoId : null);
620 $vid = $vid ? $vid : (isset($item->id) ? $item->id : null);
621 return $vid;
622 }
623 public static function get_thumbnail_url($item, $quality, $privacyStatus) {
624 $url = "";
625 if ($privacyStatus == 'private') {
626 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/private.png';
627 } elseif (isset($item->snippet->thumbnails->{$quality}->url)) {
628 $url = $item->snippet->thumbnails->{$quality}->url;
629 } elseif (isset($item->snippet->thumbnails->medium->url)) {
630 $url = $item->snippet->thumbnails->medium->url;
631 } elseif (isset($item->snippet->thumbnails->default->url)) {
632 $url = $item->snippet->thumbnails->default->url;
633 } elseif (isset($item->snippet->thumbnails->high->url)) {
634 $url = $item->snippet->thumbnails->high->url;
635 } else {
636 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/deleted-video-thumb.png';
637 }
638 return $url;
639 }
640
641 public static function compare_vid_date($a, $b) {
642 if ($a->snippet->publishedAt == $b->snippet->publishedAt) {
643 return 0;
644 }
645 return ($a->snippet->publishedAt > $b->snippet->publishedAt) ? -1 : 1;
646 }
647
648 public static function clean_api_error($raw_message) {
649 return htmlspecialchars(strip_tags(preg_replace('@&key=[^& ]+@i', '&key=*******', $raw_message)));
650 }
651
652 public static function clean_api_error_html($raw_message) {
653 $clean_html = '';
654 if ((defined('REST_REQUEST') && REST_REQUEST) || current_user_can('manage_options')) {
655 $clean_html = '<div>' . __('EmbedPress: ', 'embedpress') . self::clean_api_error($raw_message) . '</div>';
656 }
657 return $clean_html;
658 }
659
660 /** inline {@inheritdoc} */
661 public function getFakeResponse() {
662 preg_match('~v=([a-z0-9_\-]+)~i', (string) $this->url, $matches);
663
664 $embedUrl = 'https://www.youtube.com/embed/' . $matches['1'] . '?feature=oembed';
665
666 $attr = [];
667 $attr[] = 'width="{width}"';
668 $attr[] = 'height="{height}"';
669 $attr[] = 'src="' . $embedUrl . '"';
670 $attr[] = 'frameborder="0"';
671 $attr[] = 'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"';
672 $attr[] = 'allowfullscreen';
673
674 return [
675 'type' => 'video',
676 'provider_name' => 'Youtube',
677 'provider_url' => 'https://www.youtube.com',
678 'title' => 'Unknown title',
679 'html' => '<iframe ' . implode(' ', $attr) . '></iframe>',
680 ];
681 }
682
683 // public static $num = 0;
684
685
686
687 public static $x = 0;
688
689 public static function styles($params, $url){
690
691 $uniqid = '.ose-youtube.ose-uid-'.md5($url);
692
693 ob_start();
694 ?>
695 <style>
696 html {
697 scroll-behavior: smooth;
698 }
699 .ep-player-wrap .hide {
700 display: none;
701 }
702
703 .ep-gdrp-content {
704 background: #222;
705 padding: 50px 30px;
706 color: #fff;
707 }
708
709 .ep-gdrp-content a {
710 color: #fff;
711 }
712
713 .ep-youtube__content__pagination {
714 display: flex;
715 justify-content: center;
716 align-items: center;
717 margin-top: 30px;
718 gap: 10px;
719 }
720 .ep-loader-wrap {
721 margin-top: 30px;
722 display: flex;
723 justify-content: center;
724 }
725
726 .ep-youtube__content__pagination .ep-prev,
727 .ep-youtube__content__pagination .ep-next {
728 cursor: pointer;
729 border: 1px solid rgba(0, 0, 0, .1);
730 border-radius: 30px;
731 padding: 0 20px;
732 height: 40px;
733 transition: .3s;
734 display: flex;
735 align-items: center;
736 }
737 .ep-youtube__content__pagination .ep-prev:hover,
738 .ep-youtube__content__pagination .ep-next:hover{
739 background-color: #5B4E96;
740 color: #fff;
741 }
742 .ep-youtube__content__pagination .ep-page-numbers {
743 display: flex;
744 align-items: center;
745 gap: 10px;
746 flex-wrap: wrap;
747 }
748 .ep-youtube__content__pagination .ep-page-numbers > span {
749 border: 1px solid rgba(0, 0, 0, .1);
750 border-radius: 30px;
751 display: inline-block;
752 width: 45px;
753 height: 45px;
754 display: flex;
755 align-items: center;
756 justify-content: center;
757 }
758 .active__current_page{
759 background: #5B4E96;
760 color: #fff;
761 }
762
763 .ep-youtube__content__block .youtube__content__body .content__wrap {
764 margin-top: 30px;
765 display: grid;
766 grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));
767 gap: 30px;
768 }
769
770 .ep-youtube__content__block .item {
771 cursor: pointer;
772 white-space: initial;
773 }
774
775 .ep-youtube__content__block .item:hover .thumb .play-icon {
776 opacity: 1;
777 top: 50%;
778 }
779
780 .ep-youtube__content__block .item:hover .thumb:after {
781 opacity: .4;
782 z-index: 0;
783 }
784
785 .ep-youtube__content__block .thumb {
786 padding-top: 56.25%;
787 margin-bottom: 5px;
788 position: relative;
789 background: #222;
790 background-size: contain !important;
791 }
792
793 .ep-youtube__content__block .thumb:after {
794 position: absolute;
795 top: 0;
796 left: 0;
797 height: 100%;
798 width: 100%;
799 content: '';
800 background: #000;
801 opacity: 0;
802 transition: opacity .3s ease;
803 }
804
805 .ep-youtube__content__block .thumb:before {
806 position: absolute;
807 top: 0;
808 left: 0;
809 height: 100%;
810 width: 100%;
811 content: '';
812 background: #222;
813 z-index: -1;
814 }
815
816 .ep-youtube__content__block .thumb img {
817 width: 100%;
818 height: 100%;
819 object-fit: cover;
820 }
821
822 .ep-youtube__content__block .thumb .play-icon {
823 width: 50px;
824 height: auto;
825 position: absolute;
826 top: 40%;
827 left: 50%;
828 transform: translate(-50%, -50%);
829 opacity: 0;
830 transition: all .3s ease;
831 z-index: 2;
832 }
833
834 .ep-youtube__content__block .thumb .play-icon img {
835 width: 100;
836 }
837
838 .ep-youtube__content__block .body p {
839 margin-bottom: 0;
840 font-size: 15px;
841 text-align: left;
842 line-height: 1.5;
843 font-weight: 400;
844 }
845 .ep-youtube__content__block.loading .ep-youtube__content__pagination {
846 display: none;
847 }
848
849 .ep-youtube__content__block .ep-loader {
850 display: none;
851 }
852
853 .ep-youtube__content__block.loading .ep-loader {
854 display: block;
855 }
856 .ep-loader img {
857 width: 20px;
858 }
859 .is_mobile_device{
860 display: none!important;
861 }
862
863
864 .is_mobile_devic.ep-page-numbers {
865 gap: 5px;
866 }
867
868 @media only screen and (max-width: 480px) {
869 .is_desktop_device{
870 display: none!important;
871 }
872 .ep-youtube__content__pagination .ep-page-numbers > span {
873 width: 35px;
874 height: 35px;
875 }
876 .ep-youtube__content__pagination .ep-prev, .ep-youtube__content__pagination .ep-next{
877 height: 35px;
878 }
879 .is_mobile_device{
880 display: flex!important;;
881 }
882 .ep-youtube__content__pagination .ep-page-numbers {
883 gap: 5px;
884 }
885 }
886 <?php
887 $attributes_data = $params;
888
889 $is_pagination = 'flex';
890
891 $gap = '30';
892 $columns = '';
893
894 if (isset($attributes_data['ispagination']) && $attributes_data['ispagination']) {
895 $is_pagination = 'none';
896 }
897 if(isset($attributes_data['gapbetweenvideos'])){
898 $gap = $attributes_data['gapbetweenvideos'];
899 }
900 if(isset($attributes_data['columns'])){
901 $columns = $attributes_data['columns'];
902 }
903
904
905 if(!empty($columns) && (int) $columns > 0){
906 $repeatCol = 'repeat(auto-fit, minmax('.esc_html('calc('.(100 / (int) $columns).'% - '.$gap.'px)').', 1fr))';
907 }
908 else{
909 $repeatCol = 'repeat(auto-fit, minmax(calc(250px - '.$gap.'px), 1fr))';
910 }
911
912 ?>
913 <?php echo esc_attr($uniqid); ?> .ep-youtube__content__block .youtube__content__body .content__wrap {
914 gap: <?php echo esc_html($gap); ?>px !important;
915 margin-top: <?php echo esc_html($gap); ?>px !important;
916 grid-template-columns: <?php echo $repeatCol; ?>;
917 }
918 <?php echo esc_attr($uniqid); ?> .ep-youtube__content__block .ep-youtube__content__pagination {
919 display: <?php echo esc_html($is_pagination); ?>!important;
920 }
921
922 <?php
923 if($is_pagination){
924 echo esc_attr($uniqid) ?> {
925 height: 100%!important;
926 }
927 <?php
928 }
929 ?>
930 </style>
931 <?php
932 return ob_get_clean();
933 }
934 }
935