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