PluginProbe
BeyondWords – AI audio for publishers / 4.0.6
BeyondWords – AI audio for publishers v4.0.6
7.1.0 trunk 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.3.0 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 4.7.0 All 43 releases
speechkit / src / Core / LegacyPlayer.php

LegacyPlayer.php in BeyondWords – AI audio for publishers 4.0.6, at src/Core/LegacyPlayer.php

501 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Beyondwords\Wordpress\Core;
6
7 use Beyondwords\Wordpress\Component\Post\PostMetaUtils;
8 use Beyondwords\Wordpress\Component\Settings\PlayerUI\PlayerUI;
9 use Beyondwords\Wordpress\Core\Environment;
10 use Beyondwords\Wordpress\Core\CoreUtils;
11 use Symfony\Component\DomCrawler\Crawler;
12
13 /**
14 * The "Legacy" BeyondWords Player.
15 *
16 * @deprecated Scheduled for removal in v5.0
17 **/
18 class LegacyPlayer
19 {
20 /**
21 * Constructor
22 */
23 public function __construct()
24 {
25 // Actions
26 add_action('wp_enqueue_scripts', array($this, 'enqueueScripts'));
27
28 // Filters
29 add_filter('the_content', array($this, 'addPlayerToContent'));
30 add_filter('newsstand_the_content', array($this, 'addPlayerToContent'));
31 }
32
33 /**
34 * Adds the BeyondWords player to WordPress content.
35 *
36 * @since 3.0.0
37 *
38 * @param string $content WordPress content.
39 *
40 * @return string
41 */
42 public function addPlayerToContent($content)
43 {
44 return $this->playerHtml() . $content;
45 }
46
47 /**
48 * Player HTML.
49 *
50 * Displays JS SDK variant of the BeyondWords audio player, for both
51 * AMP and non-AMP content.
52 *
53 * @SuppressWarnings(PHPMD.NPathComplexity)
54 *
55 * @param WP_Post $post WordPress Post.
56 *
57 * @since 3.0.0
58 * @since 3.1.0 Added _doing_it_wrong deprecation warnings
59 *
60 * @return string
61 */
62 public function playerHtml($post = false)
63 {
64 if (! ($post instanceof \WP_Post)) {
65 $post = get_post($post);
66 }
67
68 if (! $post) {
69 return '';
70 }
71
72 if (! $this->isPlayerEnabled($post)) {
73 return '';
74 }
75
76 $projectId = PostMetaUtils::getProjectId($post->ID);
77
78 if (! $projectId) {
79 return '';
80 }
81
82 $podcastId = PostMetaUtils::getContentId($post->ID);
83
84 if (! $podcastId) {
85 return '';
86 }
87
88 // AMP or JS Player?
89 if ($this->useAmpPlayer()) {
90 $html = $this->ampPlayerHtml($post->ID, $projectId, $podcastId);
91 } else {
92 $html = $this->jsPlayerHtml($post->ID, $projectId, $podcastId);
93 }
94
95 return $html;
96 }
97
98 /**
99 * Has custom player?
100 *
101 * Checks the post content to see whether a custom player has been added.
102 *
103 * @param int $postId WordPress Post ID.
104 *
105 * @since 3.2.0
106 *
107 * @return boolean
108 */
109 public function hasCustomPlayer($postId)
110 {
111 $content = get_the_content(null, false, $postId);
112
113 $crawler = new Crawler($content);
114
115 return count($crawler->filterXPath('//div[@data-beyondwords-player="true"]')) > 0;
116 }
117
118 /**
119 * JS Player HTML.
120 *
121 * Displays the HTML required for the JS player.
122 *
123 * @param int $postId WordPress Post ID.
124 * @param int $projectId BeyondWords Project ID.
125 * @param int $podcastId BeyondWords Podcast ID.
126 *
127 * @since 3.0.0
128 * @since 3.1.0 Added speechkit_js_player_html filter
129 *
130 * @return string
131 */
132 public function jsPlayerHtml($postId, $projectId, $podcastId)
133 {
134 $html = '';
135
136 if (! $this->hasCustomPlayer($postId)) {
137 $html .= '<div data-beyondwords-player="true" contenteditable="false"></div>';
138 }
139
140 /**
141 * Filters the HTML of the BeyondWords Player.
142 *
143 * @since 4.0.0
144 *
145 * @param string $html The HTML for the JS audio player. The audio player JavaScript may
146 * fail to locate the target element if you remove or replace the
147 * default contents of this parameter.
148 * @param int $post_id WordPress post ID.
149 * @param int $project_id BeyondWords project ID.
150 * @param int $podcast_id BeyondWords podcast ID.
151 */
152 $html = apply_filters('beyondwords_player_html', $html, $postId, $projectId, $podcastId);
153
154 /**
155 * Filters the HTML of the BeyondWords JS audio player.
156 *
157 * @since 3.3.3
158 * @deprecated Scheduled for removal in v5.0
159 *
160 * @param string $html The HTML for the JS audio player. The audio player JavaScript may
161 * fail to locate the target element if you remove or replace the
162 * default contents of this parameter.
163 * @param int $post_id WordPress post ID.
164 * @param int $project_id BeyondWords project ID.
165 * @param int $podcast_id BeyondWords podcast ID.
166 */
167 $html = apply_filters('beyondwords_js_player_html', $html, $postId, $projectId, $podcastId);
168
169 return $html;
170 }
171
172 /**
173 * AMP Player HTML.
174 *
175 * Displays the HTML required for the AMP player.
176 *
177 * @param int $postId WordPress Post ID.
178 * @param int $projectId BeyondWords Project ID.
179 * @param int $podcastId BeyondWords Podcast ID.
180 *
181 * @since 3.0.0
182 * @since 3.1.0 Added speechkit_amp_player_html filter
183 *
184 * @return string
185 */
186 public function ampPlayerHtml($postId, $projectId, $podcastId)
187 {
188 $src = sprintf(Environment::getAmpPlayerUrl(), $projectId, $podcastId);
189
190 // Turn on output buffering
191 ob_start();
192
193 ?>
194 <amp-iframe
195 frameborder="0"
196 height="43"
197 layout="responsive"
198 sandbox="allow-scripts allow-same-origin allow-popups"
199 scrolling="no"
200 src="<?php echo esc_url($src); ?>"
201 width="295"
202 >
203 <amp-img
204 height="150"
205 layout="responsive"
206 placeholder
207 src="<?php echo esc_url(Environment::getAmpImgUrl()); ?>"
208 width="643"
209 ></amp-img>
210 </amp-iframe>
211 <?php
212
213 $html = ob_get_clean();
214
215 /**
216 * Filters the HTML of the BeyondWords AMP audio player.
217 *
218 * @since 3.3.3
219 *
220 * @param string $html The HTML for the AMP audio player.
221 * @param int $post_id WordPress Post ID.
222 * @param int $project_id BeyondWords Project ID.
223 * @param int $podcast_id BeyondWords Podcast ID.
224 */
225 $html = apply_filters('beyondwords_amp_player_html', $html, $postId, $projectId, $podcastId);
226
227 return $html;
228 }
229
230 /**
231 * Should we show the BeyondWords audio player?
232 *
233 * We DO NOT want to show the player if:
234 * 1. BeyondWords has been disabled in our plugin settings.
235 * 2. The current post type has not been selected in our plugin settings.
236 * 3. The current post has specifically been disabled from processing.
237 *
238 * The return value of this can be overriden with the WordPress
239 * "beyondwords_post_player_enabled" filter.
240 *
241 * @param int|WP_Post (Optional) Post ID or WP_Post object. Default is global $post.
242 *
243 * @since 3.0.0
244 * @since 3.3.4 Accept int|WP_Post as method parameter.
245 * @since 4.0.0 Check beyondwords_player_ui custom field.
246 *
247 * @return bool
248 **/
249 public function isPlayerEnabled($post = null)
250 {
251 $post = get_post($post);
252
253 if (! ($post instanceof \WP_Post)) {
254 return false;
255 }
256
257 // Assume we can show the player
258 $enabled = true;
259
260 // Has 'Display Player' been unchecked?
261 if (PostMetaUtils::getDisabled($post->ID)) {
262 $enabled = false;
263 }
264
265 /**
266 * Filters the enabled/disabled (shown/hidden) status of the player for each post.
267 *
268 * @since 3.3.3
269 *
270 * @param boolean $enabled Is the player enabled (shown) for this post?
271 * @param int $post_id WordPress post ID.
272 */
273 $enabled = apply_filters('beyondwords_post_player_enabled', $enabled, $post->ID);
274
275 return $enabled;
276 }
277
278 /**
279 * Register the JavaScript for the public-facing side of the site.
280 *
281 * @since 3.0.0
282 *
283 * @return void
284 */
285 public function enqueueScripts()
286 {
287 if (! is_singular() && ! is_admin()) {
288 return;
289 }
290
291 // JS SDK Player script, filtered by $this->scriptLoaderTag()
292 add_filter('script_loader_tag', array($this, 'scriptLoaderTag'), 10, 3);
293
294 wp_enqueue_script(
295 'beyondwords-sdk',
296 Environment::getJsSdkUrlLegacy(),
297 array(),
298 null,
299 true
300 );
301 }
302
303 /**
304 * Use the AMP player?
305 *
306 * There are multiple AMP plugins for WordPress, so multiple checks are performed.
307 *
308 * @since 3.0.7
309 *
310 * @return bool
311 */
312 public function useAmpPlayer()
313 {
314 // https://amp-wp.org/reference/function/amp_is_request/
315 if (function_exists('amp_is_request')) {
316 return \amp_is_request();
317 }
318
319 // https://ampforwp.com/tutorials/article/detect-amp-page-function/
320 if (function_exists('ampforwp_is_amp_endpoint')) {
321 return \ampforwp_is_amp_endpoint();
322 }
323
324 // https://amp-wp.org/reference/function/is_amp_endpoint/
325 if (function_exists('is_amp_endpoint')) {
326 return \is_amp_endpoint();
327 }
328
329 return false;
330 }
331
332 /**
333 * Filters the HTML script tag of an enqueued script.
334 *
335 * @param string $tag The <script> tag for the enqueued script.
336 * @param string $handle The script's registered handle.
337 * @param string $src The script's source URL.
338 *
339 * @since 3.0.0
340 *
341 * @see https://developer.wordpress.org/reference/hooks/script_loader_tag/
342 * @see https://stackoverflow.com/a/59594789
343 *
344 * @return string
345 */
346 public function scriptLoaderTag($tag, $handle, $src)
347 {
348 if ($handle === 'beyondwords-sdk') :
349 if (! $this->usePlayerJsSdk()) {
350 return '';
351 }
352
353 $post = get_post();
354
355 $params = $this->jsPlayerParams($post);
356
357 ob_start();
358 ?>
359 <script id="beyondwords-sdk" type="module">
360 //<![CDATA[
361 import BeyondWords from '<?php echo esc_url($src); ?>';
362 import { v4 as uuidv4 } from 'https://jspm.dev/uuid';
363
364 const PLAYER_SELECTOR = 'div[data-beyondwords-player]:not([data-beyondwords-init])';
365
366 const players = Array.from(
367 document.querySelectorAll(PLAYER_SELECTOR)
368 );
369
370 await Promise.all(players.map((item, index) => {
371 const playerId = `beyondwords-player-${uuidv4()}`;
372
373 item.setAttribute('id', playerId);
374
375 return BeyondWords.player({
376 ...<?php echo wp_json_encode($params, JSON_FORCE_OBJECT); ?>,
377 "renderNode": playerId,
378 }).then((player) => {
379 item.setAttribute('data-beyondwords-init', 'true');
380 console.log(`🔊 #${playerId} is initialized`);
381 }).catch(err => {
382 console.error(err);
383 });
384 }));
385 //]]>
386 </script>
387 <?php
388 return ob_get_clean();
389 endif;
390
391 return $tag;
392 }
393
394 /**
395 * JavaScript SDK parameters.
396 *
397 * Note that the default return value for this method is an associative array, but
398 * the HTML output will be forced to an object due to `wp_json_encode($params, JSON_FORCE_OBJECT)`
399 * in `Player::scriptLoaderTag()`.
400 *
401 * @since 3.1.0
402 *
403 * @see https://docs.beyondwords.io/docs/javascript-sdk-automatic-player
404 *
405 * @param WP_Post $post WordPress Post.
406 *
407 * @return array
408 */
409 public function jsPlayerParams($post)
410 {
411 if (!($post instanceof \WP_Post)) {
412 return [];
413 }
414
415 $projectId = PostMetaUtils::getProjectId($post->ID);
416 $podcastId = PostMetaUtils::getContentId($post->ID);
417
418 $skBackend = Environment::getBackendUrl();
419 $skBackendApi = Environment::getApiUrl();
420
421 $params = [
422 'projectId' => (int)$projectId,
423 'podcastId' => $podcastId,
424 ];
425
426 if (get_option('beyondwords_player_size') === 'medium') {
427 $params['playerType'] = 'manual';
428 }
429
430 if (strlen($skBackend)) {
431 $params['skBackend'] = esc_url($skBackend);
432 }
433
434 if (is_admin()) {
435 $params['processingStatus'] = true;
436 $params['apiWriteKey'] = get_option('beyondwords_api_key', '');
437
438 if (strlen($skBackendApi)) {
439 $params['skBackendApi'] = esc_url($skBackendApi);
440 }
441 }
442
443 if (defined('BEYONDWORDS_DEBUG') && BEYONDWORDS_DEBUG) {
444 $params['debug'] = true;
445 }
446
447 /**
448 * Filters the BeyondWords JavaScript SDK parameters.
449 *
450 * @since 3.3.3
451 *
452 * @param array The default JS SDK params.
453 */
454 $params = apply_filters('beyondwords_js_player_params', $params);
455
456 return $params;
457 }
458
459 /**
460 * Use Player JS SDK?
461 *
462 * @since 3.0.7
463 *
464 * @return string
465 */
466 public function usePlayerJsSdk()
467 {
468 // AMP requests don't use the Player JS SDK
469 if ($this->useAmpPlayer()) {
470 return false;
471 }
472
473 // Gutenberg has a dedicated React component for the player
474 if (CoreUtils::isGutenbergPage()) {
475 return false;
476 }
477
478 // Disable audio player in Preview, because we have not sent updates to BeyondWords API yet
479 if (function_exists('is_preview') && is_preview()) {
480 return false;
481 }
482
483 $post = get_post();
484
485 if (! $post) {
486 return false;
487 }
488
489 $projectId = PostMetaUtils::getProjectId($post->ID);
490 if (! $projectId) {
491 return false;
492 }
493
494 $podcastId = PostMetaUtils::getContentId($post->ID);
495 if (! $podcastId) {
496 return false;
497 }
498
499 return true;
500 }
501 }