PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 3.9.10
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v3.9.10
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 2 years ago Calendly.php 2 years ago Giphy.php 2 years ago GitHub.php 2 years ago GoogleDocs.php 2 years ago GoogleDrive.php 2 years ago GoogleMaps.php 2 years ago Gumroad.php 2 years ago NRKRadio.php 2 years ago OpenSea.php 2 years ago SelfHosted.php 2 years ago TikTok.php 2 years ago Twitch.php 2 years ago Wrapper.php 2 years ago Youtube.php 2 years ago index.html 7 years ago
Youtube.php
947 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 $channelId = 'unknown_id'; // temporarily assigned a placeholder value for demonstration purposes
73
74 if (empty($url)) {
75 $url = $this->url;
76 }
77 preg_match('~\/(channel|c|user)\/(.+)~i', (string) $url, $matches);
78 if(empty($matches[1])){
79 preg_match('~(?:https?:\/\/)?(?:www\.)?(?:youtube.com\/)(\w+)[^?\/]*$~i', (string) $url, $matches);
80 if(!empty($matches[1])){
81 return [
82 "type" => 'user',
83 "id" => $matches[1],
84 ];
85 }
86 }
87
88 // $channel___id = $this->get_channel_id_by_handler('adin');
89
90 // echo 'akash';
91 // print_r($channel___id); die;
92
93 if(empty($matches[1])){
94 preg_match('~\/(@)(\w+)~i', (string) $url, $matches);
95 if(!empty($matches[1])){
96 if(!empty($this->get_youtube_handler($this->url))){
97 if(!empty($this->get_channel_id_by_handler($this->get_youtube_handler($this->url)))){
98 $channelId = $this->get_channel_id_by_handler($this->get_youtube_handler($this->url));
99 }
100 }
101 return [
102 "type" => 'user',
103 "id" => $channelId,
104 ];
105 }
106 }
107 return [
108 "type" => isset($matches[1]) ? $matches[1] : '',
109 "id" => isset($matches[2]) ? $matches[2] : '',
110 ];
111 }
112
113 /** inline {@inheritdoc} */
114 public function getEndpoint() {
115 if ($this->isChannel()) {
116 $apiEndpoint = 'https://www.googleapis.com/youtube/v3/channels';
117 return $apiEndpoint;
118 }
119 return (string) $this->endpoint;
120 }
121
122 protected static function get_api_key() {
123 $settings = (array) get_option(EMBEDPRESS_PLG_NAME . ':youtube', []);
124 return !empty($settings['api_key']) ? $settings['api_key'] : '';
125 }
126
127 protected static function get_pagesize() {
128 $settings = (array) get_option(EMBEDPRESS_PLG_NAME . ':youtube', []);
129 return !empty($settings['pagesize']) ? $settings['pagesize'] : '';
130 }
131
132 /** inline {@inheritdoc} */
133 public function getParams() {
134 $params = parent::getParams();
135 if ($this->isChannel() && self::get_api_key()) {
136 $channel = $this->getChannel();
137 $params['part'] = 'contentDetails,snippet';
138 $params['key'] = self::get_api_key();
139 if ($channel['type'] == 'c') {
140 $params['forUsername'] = $channel['id'];
141 } else {
142 $params['id'] = $channel['id'];
143 }
144 unset($params['url']);
145 }
146 return $params;
147 }
148
149 /**
150 * Builds a valid Oembed query string based on the given parameters,
151 * Since this method uses the http_build_query function, there is no
152 * need to pass urlencoded parameters, http_build_query already does
153 * this for us.
154 *
155 * @param string $endpoint The Url to the Oembed endpoint
156 * @param array $params Parameters for the query string
157 * @return string
158 */
159
160 protected function constructUrl($endpoint, array $params = array())
161 {
162 $endpoint = self::$channel_endpoint . $endpoint;
163
164 return $endpoint . ((strpos($endpoint, '?') === false) ? '?' : '&') . http_build_query(array_filter($params));
165 }
166
167 public function getStaticResponse() {
168 $results = [
169 "title" => "",
170 "type" => "video",
171 'provider_name' => $this->getProviderName(),
172 "provider_url" => "https://www.youtube.com/",
173 'html' => '',
174 ];
175
176 $params = $this->getParams();
177
178 if (preg_match("/^https?:\/\/(?:www\.)?youtube\.com\/channel\/([\w-]+)\/live$/", $this->url, $matches) || $this->validateTYLiveUrl($this->url)) {
179
180 if(!empty($matches[1])){
181 $channelId = $matches[1];
182 }
183
184 if(!empty($this->get_youtube_handler($this->url))){
185 if(!empty($this->get_channel_id_by_handler($this->get_youtube_handler($this->url)))){
186 $channelId = $this->get_channel_id_by_handler($this->get_youtube_handler($this->url));
187 }
188 }
189
190 $embedUrl = 'https://www.youtube.com/embed/live_stream?channel='.$channelId.'&feature=oembed';
191
192 $attr = [];
193 $attr[] = 'width="'.esc_attr($params['maxheight']).'"';
194 $attr[] = 'height="'.esc_attr($params['maxheight']).'";';
195 $attr[] = 'src="' . esc_url($embedUrl) . '"';
196 $attr[] = 'frameborder="0"';
197 $attr[] = 'allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"';
198 $attr[] = 'allowfullscreen';
199
200 $results['html'] = '<iframe ' . implode(' ', $attr) . '></iframe>';
201 }
202 else if($this->isChannel()){
203 $channel = $this->getChannelGallery();
204 $results = array_merge($results, $channel);
205 }
206 return $results;
207 }
208
209 public function getChannelPlaylist(){
210 $result = [
211 "playlistID" => '',
212 "title" => '',
213 ];
214 $channel = $this->getChannel();
215 $channel_url = $this->constructUrl('channels', $this->getParams());
216 $transient_key = 'ep_embed_youtube_channel_playlist_id_' . md5($channel_url);
217 $jsonResult = get_transient($transient_key);
218
219 if(!empty($jsonResult)){
220 return $jsonResult;
221 }
222
223 if($channel['type'] == 'user' || $channel['type'] == 'c'){
224 $this->getChannelIDbyUsername();
225 $channel_url = $this->constructUrl('channels', $this->getParams());
226 }
227
228 if (empty(self::get_api_key())) {
229 $result['error'] = true;
230 $result['html'] = self::get_api_key_error_message();
231 return $result;
232 }
233
234 $apiResult = wp_remote_get($channel_url, array('timeout' => self::$curltimeout));
235 if (is_wp_error($apiResult)) {
236 $result['error'] = true;
237 $result['html'] = self::clean_api_error_html($apiResult->get_error_message(), true);
238 set_transient($transient_key, $result, 10);
239 return $result;
240 }
241 $jsonResult = json_decode($apiResult['body']);
242
243
244 if (isset($jsonResult->error)) {
245 $result['error'] = true;
246 if (isset($jsonResult->error->message)) {
247 $result['html'] = self::clean_api_error_html($jsonResult->error->message, true);
248 }
249 else{
250 $result['html'] = self::clean_api_error_html(__('Sorry, there may be an issue with your YouTube API key.', 'embedpress'));
251 }
252 set_transient($transient_key, $result, MINUTE_IN_SECONDS);
253 return $result;
254 }
255 elseif(!empty($jsonResult->items[0]->contentDetails->relatedPlaylists->uploads)){
256 $result['playlistID'] = $jsonResult->items[0]->contentDetails->relatedPlaylists->uploads;
257 $result['title'] = isset($jsonResult->items[0]->snippet->title) ? $jsonResult->items[0]->snippet->title : '';
258 set_transient($transient_key, $result, DAY_IN_SECONDS);
259 }
260
261 return $result;
262 }
263
264 public function get_youtube_handler($url){
265 // preg_match('/^https:\/\/www.youtube.com\/@(.+)\/live$/i', $url, $matches);
266 preg_match('/^https:\/\/www.youtube.com\/@([^\/?]+)/i', $url, $matches);
267
268
269 $handle_name = '';
270 if(!empty($matches[1])){
271 $handle_name = $matches[1];
272 }
273
274 return $handle_name;
275 }
276
277 public function getChannelIDbyUsername(){
278 $url = $this->getUrl();
279 $apiResult = wp_remote_get($url, array('timeout' => self::$curltimeout));
280
281 if (!is_wp_error($apiResult)) {
282 $channel_html = $apiResult['body'];
283 preg_match("/<meta\s+itemprop=[\"']channelId[\"']\s+content=[\"'](.*?)[\"']\/?>/", $channel_html, $matches);
284 if(!empty($matches[1])){
285 $url = "https://www.youtube.com/channel/{$matches[1]}";
286 $this->url = $this->normalizeUrl(new Url($url));
287 }
288 }
289 }
290
291
292 public function get_channel_id_by_handler($handle)
293 {
294 $transient_key = 'channel_id_' . md5($handle);
295 $channel_id = get_transient($transient_key);
296
297 if (false === $channel_id) {
298 $ch = curl_init();
299
300 $channel_handle = "https://www.youtube.com/@{$handle}";
301
302 curl_setopt($ch, CURLOPT_URL, $channel_handle);
303 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
304
305 $response = curl_exec($ch);
306
307 if (curl_errno($ch)) {
308 return 'cURL error: ' . curl_error($ch);
309 }
310
311 curl_close($ch);
312
313 $pattern = '/(<link rel="canonical" href="https:\/\/www\.youtube\.com\/channel\/)(.{1,50})(">)/';
314 if (preg_match($pattern, $response, $matches)) {
315 $channel_id = $matches[2];
316 set_transient($transient_key, $channel_id, 30 * DAY_IN_SECONDS);
317
318 return $channel_id;
319 } else {
320 return "Not a channel URL";
321 }
322 } else {
323 return $channel_id;
324 }
325 }
326
327
328 /** inline {@inheritdoc} */
329 public function getChannelGallery() {
330 $response = [];
331 $channel = $this->getChannelPlaylist();
332 if(!empty($channel['error'])){
333 return $channel;
334 }
335 if (!empty($channel["playlistID"])) {
336 $params = $this->getParams();
337 $the_playlist_id = $channel["playlistID"];
338 $rel = 'https://www.youtube.com/embed?listType=playlist&list=' . esc_attr($the_playlist_id);
339 $title = $channel['title'];
340 $main_iframe = "";
341 $gallery_args = [
342 'playlistId' => $the_playlist_id,
343 ];
344 if(!empty($params['pagesize'])){
345 $gallery_args['pagesize'] = $params['pagesize'];
346 }
347 $gallery = self::get_gallery_page($gallery_args);
348
349 if (!empty($gallery->first_vid)) {
350 $rel = "https://www.youtube.com/embed/{$gallery->first_vid}?feature=oembed";
351 $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>";
352 }
353 if($gallery->html && $this->validateTYLiveUrl($this->getUrl())){
354 $styles = self::styles($params, $this->getUrl());
355 return [
356 "title" => $title,
357 "html" => "<div class='ep-player-wrap'>$main_iframe $styles</div>",
358 ];
359 }
360 if($gallery->html){
361 $styles = self::styles($params, $this->getUrl());
362 return [
363 "title" => $title,
364 "html" => "<div class='ep-player-wrap'>$main_iframe {$gallery->html} $styles</div>",
365 ];
366 }
367 }
368 elseif ($this->isChannel() && empty(self::get_api_key()) && current_user_can('manage_options')) {
369 return [
370 "html" => "<div class='ep-player-wrap'>" . __('Please enter your YouTube API key to embed YouTube Channel.', 'embedpress') . "</div>",
371 ];
372 }
373
374 return $response;
375 }
376
377 /**
378 * Undocumented function
379 *
380 * @param array $options
381 * @return object
382 */
383 public static function get_gallery_page($options) {
384 $nextPageToken = '';
385 $prevPageToken = '';
386 $gallobj = new \stdClass();
387 $options = wp_parse_args($options, [
388 'playlistId' => '',
389 'pageToken' => '',
390 'pagesize' => self::get_pagesize() ? self::get_pagesize() : 6,
391 'currentpage' => '',
392 'columns' => 3,
393 'thumbnail' => 'medium',
394 'gallery' => true,
395 'autonext' => true,
396 'thumbplay' => true,
397 'apiKey' => self::get_api_key(),
398 'hideprivate' => '',
399 ]);
400 $options['pagesize'] = $options['pagesize'] > 50 ? 50 : $options['pagesize'];
401 $options['pagesize'] = $options['pagesize'] < 1 ? 1 : $options['pagesize'];
402
403 if (empty($options['apiKey'])) {
404 $gallobj->html = self::get_api_key_error_message();
405 return $gallobj;
406 }
407
408 $apiEndpoint = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,status&playlistId=' . $options['playlistId']
409 . '&maxResults=' . $options['pagesize']
410 . '&key=' . $options['apiKey'];
411 if ($options['pageToken'] != null) {
412 $apiEndpoint .= '&pageToken=' . $options['pageToken'];
413 }
414
415 $transient_key = 'ep_embed_youtube_channel_' . md5($apiEndpoint);
416 $gallobj->transient_key = $transient_key;
417 $jsonResult = get_transient($transient_key);
418 if (empty($jsonResult)) {
419 $apiResult = wp_remote_get($apiEndpoint, array('timeout' => self::$curltimeout));
420 if (is_wp_error($apiResult)) {
421 $gallobj->html = self::clean_api_error_html($apiResult->get_error_message(), true);
422 return $gallobj;
423 }
424 $jsonResult = json_decode($apiResult['body']);
425 if (empty($jsonResult->error)) {
426 set_transient($transient_key, $jsonResult, MINUTE_IN_SECONDS * 20);
427 }
428 else{
429 set_transient($transient_key, $jsonResult, 10);
430 }
431 }
432
433
434 if (isset($jsonResult->error)) {
435 if(!empty($jsonResult->error->errors[0]->reason) && $jsonResult->error->errors[0]->reason == 'playlistNotFound'){
436 $gallobj->html = self::clean_api_error_html(__('There is nothing on the playlist.', 'embedpress'));
437 return $gallobj;
438 }
439 if (isset($jsonResult->error->message)) {
440 $gallobj->html = self::clean_api_error_html($jsonResult->error->message);
441 return $gallobj;
442 }
443 $gallobj->html = self::clean_api_error_html(__('Sorry, there may be an issue with your YouTube API key.', 'embedpress'));
444 return $gallobj;
445 }
446
447
448
449 $resultsPerPage = $jsonResult->pageInfo->resultsPerPage;
450 $totalResults = $jsonResult->pageInfo->totalResults;
451 $totalPages = ceil($totalResults / $resultsPerPage);
452 if (isset($jsonResult->nextPageToken)) {
453 $nextPageToken = $jsonResult->nextPageToken;
454 }
455
456 if (isset($jsonResult->prevPageToken)) {
457 $prevPageToken = $jsonResult->prevPageToken;
458 }
459
460
461 if (!empty($jsonResult->items) && is_array($jsonResult->items)) :
462 if($options['gallery'] === "false"){
463 $gallobj->html = "";
464 if(count($jsonResult->items) === 1){
465 $gallobj->first_vid = self::get_id($jsonResult->items[0]);
466 }
467 return $gallobj;
468 }
469
470 if(count($jsonResult->items) === 1 && empty($nextPageToken) && empty($prevPageToken)){
471 $gallobj->first_vid = self::get_id($jsonResult->items[0]);
472 $gallobj->html = "";
473 return $gallobj;
474 }
475
476 if (strpos($options['playlistId'], 'UU') === 0) {
477 // sort only channels
478 usort($jsonResult->items, array(get_class(), 'compare_vid_date')); // sorts in place
479 }
480
481 ob_start();
482 ?>
483 <div class="ep-youtube__content__block" data-unique-id="<?php echo wp_rand(); ?>">
484 <div class="youtube__content__body">
485 <div class="content__wrap">
486 <?php foreach ($jsonResult->items as $item) : ?>
487 <?php
488 $privacyStatus = isset($item->status->privacyStatus) ? $item->status->privacyStatus : null;
489 $thumbnail = self::get_thumbnail_url($item, $options['thumbnail'], $privacyStatus);
490 $vid = self::get_id($item);
491 if (empty($gallobj->first_vid)) {
492 $gallobj->first_vid = $vid;
493 }
494 if ($privacyStatus == 'private' && $options['hideprivate']) {
495 continue;
496 }
497 ?>
498 <div class="item" data-vid="<?php echo $vid; ?>">
499 <div class="thumb" style="background: <?php echo "url({$thumbnail}) no-repeat center"; ?>">
500 <div class="play-icon">
501 <img src="<?php echo esc_url(EMBEDPRESS_URL_ASSETS . 'images/youtube/youtube-play.png'); ?>" alt="">
502 </div>
503 </div>
504 <div class="body">
505 <p><?php echo $item->snippet->title; ?></p>
506 </div>
507 </div>
508
509 <?php endforeach; ?>
510 <div class="item" style="height: 0"></div>
511 </div>
512
513
514 <?php if ($totalPages > 1) : ?>
515 <div class="ep-youtube__content__pagination <?php echo (empty($prevPageToken) && empty($nextPageToken)) ? ' hide ' : ''; ?>">
516 <div
517 class="ep-prev" <?php echo empty($prevPageToken) ? ' style="display:none" ' : ''; ?>
518 data-playlistid="<?php echo esc_attr($options['playlistId']) ?>"
519 data-pagetoken="<?php echo esc_attr($prevPageToken) ?>"
520 data-pagesize="<?php echo intval($options['pagesize']) ?>"
521 >
522 <span><?php _e("Prev", "embedpress"); ?></span>
523 </div>
524 <div class="is_desktop_device ep-page-numbers <?php echo $totalPages > 1 ? '' : 'hide'; ?>">
525 <?php
526
527 $numOfPages = $totalPages;
528 $renderedEllipses = false;
529
530 $currentPage = !empty($options['currentpage'])?$options['currentpage'] : 1;
531
532 for($i = 1; $i<=$numOfPages; $i++)
533 {
534 //render pages 1 - 3
535 if($i < 4) {
536 //render link
537 $is_current = $i == (int)$currentPage? "active__current_page" : "";
538
539 echo wp_kses_post("<span class='page-number $is_current' data-page='$i'>$i</span>");
540
541 }
542
543 //render current page number
544 else if($i == (int)$currentPage) {
545 //render link
546 echo wp_kses_post('<span class="page-number active__current_page" data-page="'.$i.'">'.$i.'</span>');
547 //reset ellipses
548 $renderedEllipses = false;
549 }
550
551 //last page number
552 else if ($i >= $numOfPages - 1) {
553 //render link
554 echo wp_kses_post('<span class="page-number" data-page="'.$i.'">'.$i.'</span>');
555 }
556
557 //make sure you only do this once per ellipses group
558 else {
559 if (!$renderedEllipses){
560 print("...");
561 $renderedEllipses = true;
562 }
563 }
564 }
565 ?>
566
567 </div>
568
569 <div class="is_mobile_device ep-page-numbers <?php echo $totalPages > 1 ? '' : 'hide'; ?>">
570 <?php
571
572 $numOfPages = $totalPages;
573 $renderedEllipses = false;
574
575 $currentPage = !empty($options['currentpage'])?$options['currentpage'] : 1;
576
577 for($i = 1; $i<=$numOfPages; $i++)
578 {
579
580 //render current page number
581 if($i == (int)$currentPage) {
582 //render link
583 echo wp_kses_post('<span class="page-number-mobile" data-page="'.$i.'">'.$i.'</span>');
584 //reset ellipses
585 $renderedEllipses = false;
586 }
587
588 //last page number
589 else if ($i >= $numOfPages ) {
590 //render link
591 echo wp_kses_post('...<span class="page-number-mobile" data-page="'.$i.'">'.$i.'</span>');
592 }
593 }
594 ?>
595
596 </div>
597
598
599 <div
600 class="ep-next " <?php echo empty($nextPageToken) ? ' style="display:none" ' : ''; ?>
601 data-playlistid="<?php echo esc_attr($options['playlistId']) ?>"
602 data-pagetoken="<?php echo esc_attr($nextPageToken) ?>"
603 data-pagesize="<?php echo intval($options['pagesize']) ?>"
604 >
605 <span><?php _e("Next ", "embedpress"); ?> </span>
606 </div>
607 </div>
608 <?php endif; ?>
609
610 <div class="ep-loader-wrap">
611 <div class="ep-loader"><img alt="loading" src="<?php echo esc_url(EMBEDPRESS_URL_ASSETS . 'images/youtube/spin.gif'); ?>"></div>
612 </div>
613
614 </div>
615 </div>
616 <?php
617 $gallobj->html = ob_get_clean();
618 else:
619 $gallobj->html = self::clean_api_error_html(__("There is nothing on the playlist.", 'embedpress'));
620 endif;
621
622 return $gallobj;
623 }
624
625 public static function get_api_key_error_message(){
626 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>';
627 }
628
629 public static function get_id($item){
630 $vid = isset($item->snippet->resourceId->videoId) ? $item->snippet->resourceId->videoId : null;
631 $vid = $vid ? $vid : (isset($item->id->videoId) ? $item->id->videoId : null);
632 $vid = $vid ? $vid : (isset($item->id) ? $item->id : null);
633 return $vid;
634 }
635 public static function get_thumbnail_url($item, $quality, $privacyStatus) {
636 $url = "";
637 if ($privacyStatus == 'private') {
638 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/private.png';
639 } elseif (isset($item->snippet->thumbnails->{$quality}->url)) {
640 $url = $item->snippet->thumbnails->{$quality}->url;
641 } elseif (isset($item->snippet->thumbnails->medium->url)) {
642 $url = $item->snippet->thumbnails->medium->url;
643 } elseif (isset($item->snippet->thumbnails->default->url)) {
644 $url = $item->snippet->thumbnails->default->url;
645 } elseif (isset($item->snippet->thumbnails->high->url)) {
646 $url = $item->snippet->thumbnails->high->url;
647 } else {
648 $url = EMBEDPRESS_URL_ASSETS . 'images/youtube/deleted-video-thumb.png';
649 }
650 return $url;
651 }
652
653 public static function compare_vid_date($a, $b) {
654 if ($a->snippet->publishedAt == $b->snippet->publishedAt) {
655 return 0;
656 }
657 return ($a->snippet->publishedAt > $b->snippet->publishedAt) ? -1 : 1;
658 }
659
660 public static function clean_api_error($raw_message) {
661 return htmlspecialchars(strip_tags(preg_replace('@&key=[^& ]+@i', '&key=*******', $raw_message)));
662 }
663
664 public static function clean_api_error_html($raw_message) {
665 $clean_html = '';
666 if ((defined('REST_REQUEST') && REST_REQUEST) || current_user_can('manage_options')) {
667 $clean_html = '<div>' . __('EmbedPress: ', 'embedpress') . self::clean_api_error($raw_message) . '</div>';
668 }
669 return $clean_html;
670 }
671
672 /** inline {@inheritdoc} */
673 public function getFakeResponse() {
674 preg_match('~v=([a-z0-9_\-]+)~i', (string) $this->url, $matches);
675
676 $embedUrl = 'https://www.youtube.com/embed/' . $matches['1'] . '?feature=oembed';
677
678 $attr = [];
679 $attr[] = 'width="{width}"';
680 $attr[] = 'height="{height}"';
681 $attr[] = 'src="' . esc_url($embedUrl) . '"';
682 $attr[] = 'frameborder="0"';
683 $attr[] = 'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"';
684 $attr[] = 'allowfullscreen';
685
686 return [
687 'type' => 'video',
688 'provider_name' => 'Youtube',
689 'provider_url' => 'https://www.youtube.com',
690 'title' => 'Unknown title',
691 'html' => '<iframe ' . implode(' ', $attr) . '></iframe>',
692 ];
693 }
694
695 // public static $num = 0;
696
697
698
699 public static $x = 0;
700
701 public static function styles($params, $url){
702
703 $uniqid = '.ose-youtube.ose-uid-'.md5($url);
704
705 ob_start();
706 ?>
707 <style>
708 html {
709 scroll-behavior: smooth;
710 }
711 .ep-player-wrap .hide {
712 display: none;
713 }
714
715 .ep-gdrp-content {
716 background: #222;
717 padding: 50px 30px;
718 color: #fff;
719 }
720
721 .ep-gdrp-content a {
722 color: #fff;
723 }
724
725 .ep-youtube__content__pagination {
726 display: flex;
727 justify-content: center;
728 align-items: center;
729 margin-top: 30px;
730 gap: 10px;
731 }
732 .ep-loader-wrap {
733 margin-top: 30px;
734 display: flex;
735 justify-content: center;
736 }
737
738 .ep-youtube__content__pagination .ep-prev,
739 .ep-youtube__content__pagination .ep-next {
740 cursor: pointer;
741 border: 1px solid rgba(0, 0, 0, .1);
742 border-radius: 30px;
743 padding: 0 20px;
744 height: 40px;
745 transition: .3s;
746 display: flex;
747 align-items: center;
748 }
749 .ep-youtube__content__pagination .ep-prev:hover,
750 .ep-youtube__content__pagination .ep-next:hover{
751 background-color: #5B4E96;
752 color: #fff;
753 }
754 .ep-youtube__content__pagination .ep-page-numbers {
755 display: flex;
756 align-items: center;
757 gap: 10px;
758 flex-wrap: wrap;
759 }
760 .ep-youtube__content__pagination .ep-page-numbers > span {
761 border: 1px solid rgba(0, 0, 0, .1);
762 border-radius: 30px;
763 display: inline-block;
764 width: 45px;
765 height: 45px;
766 display: flex;
767 align-items: center;
768 justify-content: center;
769 }
770 .active__current_page{
771 background: #5B4E96;
772 color: #fff;
773 }
774
775 .ep-youtube__content__block .youtube__content__body .content__wrap {
776 margin-top: 30px;
777 display: grid;
778 grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));
779 gap: 30px;
780 }
781
782 .ep-youtube__content__block .item {
783 cursor: pointer;
784 white-space: initial;
785 }
786
787 .ep-youtube__content__block .item:hover .thumb .play-icon {
788 opacity: 1;
789 top: 50%;
790 }
791
792 .ep-youtube__content__block .item:hover .thumb:after {
793 opacity: .4;
794 z-index: 0;
795 }
796
797 .ep-youtube__content__block .thumb {
798 padding-top: 56.25%;
799 margin-bottom: 5px;
800 position: relative;
801 background: #222;
802 background-size: contain !important;
803 }
804
805 .ep-youtube__content__block .thumb:after {
806 position: absolute;
807 top: 0;
808 left: 0;
809 height: 100%;
810 width: 100%;
811 content: '';
812 background: #000;
813 opacity: 0;
814 transition: opacity .3s ease;
815 }
816
817 .ep-youtube__content__block .thumb:before {
818 position: absolute;
819 top: 0;
820 left: 0;
821 height: 100%;
822 width: 100%;
823 content: '';
824 background: #222;
825 z-index: -1;
826 }
827
828 .ep-youtube__content__block .thumb img {
829 width: 100%;
830 height: 100%;
831 object-fit: cover;
832 }
833
834 .ep-youtube__content__block .thumb .play-icon {
835 width: 50px;
836 height: auto;
837 position: absolute;
838 top: 40%;
839 left: 50%;
840 transform: translate(-50%, -50%);
841 opacity: 0;
842 transition: all .3s ease;
843 z-index: 2;
844 }
845
846 .ep-youtube__content__block .thumb .play-icon img {
847 width: 100;
848 }
849
850 .ep-youtube__content__block .body p {
851 margin-bottom: 0;
852 font-size: 15px;
853 text-align: left;
854 line-height: 1.5;
855 font-weight: 400;
856 }
857 .ep-youtube__content__block.loading .ep-youtube__content__pagination {
858 display: none;
859 }
860
861 .ep-youtube__content__block .ep-loader {
862 display: none;
863 }
864
865 .ep-youtube__content__block.loading .ep-loader {
866 display: block;
867 }
868 .ep-loader img {
869 width: 20px;
870 }
871 .is_mobile_device{
872 display: none!important;
873 }
874
875
876 .is_mobile_devic.ep-page-numbers {
877 gap: 5px;
878 }
879
880 @media only screen and (max-width: 480px) {
881 .is_desktop_device{
882 display: none!important;
883 }
884 .ep-youtube__content__pagination .ep-page-numbers > span {
885 width: 35px;
886 height: 35px;
887 }
888 .ep-youtube__content__pagination .ep-prev, .ep-youtube__content__pagination .ep-next{
889 height: 35px;
890 }
891 .is_mobile_device{
892 display: flex!important;;
893 }
894 .ep-youtube__content__pagination .ep-page-numbers {
895 gap: 5px;
896 }
897 }
898 <?php
899 $attributes_data = $params;
900
901 $is_pagination = 'flex';
902
903 $gap = '30';
904 $columns = '';
905
906 if (isset($attributes_data['ispagination']) && $attributes_data['ispagination']) {
907 $is_pagination = 'none';
908 }
909 if(isset($attributes_data['gapbetweenvideos'])){
910 $gap = $attributes_data['gapbetweenvideos'];
911 }
912 if(isset($attributes_data['columns'])){
913 $columns = $attributes_data['columns'];
914 }
915
916
917 if(!empty($columns) && (int) $columns > 0){
918 $repeatCol = 'repeat(auto-fit, minmax('.esc_html('calc('.(100 / (int) $columns).'% - '.$gap.'px)').', 1fr))';
919 }
920 else{
921 $repeatCol = 'repeat(auto-fit, minmax(calc(250px - '.$gap.'px), 1fr))';
922 }
923
924 ?>
925 <?php echo esc_attr($uniqid); ?> .ep-youtube__content__block .youtube__content__body .content__wrap {
926 gap: <?php echo esc_html($gap); ?>px !important;
927 margin-top: <?php echo esc_html($gap); ?>px !important;
928 grid-template-columns: <?php echo $repeatCol; ?>;
929 }
930 <?php echo esc_attr($uniqid); ?> .ep-youtube__content__block .ep-youtube__content__pagination {
931 display: <?php echo esc_html($is_pagination); ?>!important;
932 }
933
934 <?php
935 if($is_pagination){
936 echo esc_attr($uniqid) ?> {
937 height: 100%!important;
938 }
939 <?php
940 }
941 ?>
942 </style>
943 <?php
944 return ob_get_clean();
945 }
946 }
947