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 / Component / Post / PostMetaUtils.php

PostMetaUtils.php in BeyondWords – AI audio for publishers 4.0.6, at src/Component/Post/PostMetaUtils.php

415 lines 12.9 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\Component\Post;
6
7 /**
8 * BeyondWords Post Meta (Custom Field) Utilities.
9 *
10 * @package Beyondwords
11 * @subpackage Beyondwords/includes
12 * @author Stuart McAlpine <stu@beyondwords.io>
13 * @since 3.5.0
14 */
15 class PostMetaUtils
16 {
17 public const WP_ERROR_FORMAT = 'WP_Error [%s] %s';
18
19 /**
20 * Get "renamed" Post Meta.
21 *
22 * We previously saved custom fields with a prefix of `speechkit_*` and we now
23 * save them with a prefix of `beyondwords_*`.
24 *
25 * This method checks both prefixes, copying `speechkit_*` data to `beyondwords_*`.
26 *
27 * @since 3.7.0
28 *
29 * @param int $postId Post ID.
30 * @param string $name Custom field name, without the prefix.
31 *
32 * @return string
33 */
34 public static function getRenamedPostMeta($postId, $name)
35 {
36 if (metadata_exists('post', $postId, 'beyondwords_' . $name)) {
37 return get_post_meta($postId, 'beyondwords_' . $name, true);
38 }
39
40 if (metadata_exists('post', $postId, 'speechkit_' . $name)) {
41 $value = get_post_meta($postId, 'speechkit_' . $name, true);
42
43 // Migrate over to newer `beyondwords_*` format
44 update_post_meta($postId, 'beyondwords_' . $name, $value);
45
46 return $value;
47 }
48
49 return '';
50 }
51
52 /**
53 * Get the BeyondWords metadata for a Post.
54 */
55 public static function getAllBeyondwordsMetadata($postId)
56 {
57 $keysToCheck = [
58 'beyondwords_generate_audio',
59 'beyondwords_project_id',
60 'beyondwords_content_id',
61 'beyondwords_podcast_id',
62 'beyondwords_content_id',
63 'beyondwords_language_id',
64 'beyondwords_body_voice_id',
65 'beyondwords_title_voice_id',
66 'beyondwords_summary_voice_id',
67 'beyondwords_error_message',
68 'beyondwords_disabled',
69 'beyondwords_podcast_id',
70 'publish_post_to_speechkit',
71 'speechkit_generate_audio',
72 'speechkit_project_id',
73 'speechkit_podcast_id',
74 'speechkit_error_message',
75 'speechkit_disabled',
76 'speechkit_access_key',
77 'speechkit_error',
78 'speechkit_info',
79 'speechkit_response',
80 'speechkit_retries',
81 'speechkit_status',
82 '_speechkit_link',
83 '_speechkit_text',
84 ];
85
86 $metadata = has_meta($postId);
87
88 $metadata = array_filter($metadata, function ($item) use ($keysToCheck) {
89 return in_array($item['meta_key'], $keysToCheck);
90 });
91
92 // Prepend the WordPress Post ID to the meta data
93 array_unshift($metadata, [
94 'meta_id' => null,
95 'meta_key' => 'post_id',
96 'meta_value' => $postId, // phpcs:ignore
97 ]);
98
99 return $metadata;
100 }
101
102 /**
103 * Get the Content ID for a WordPress Post.
104 *
105 * Over time there have been various approaches to storing the Content ID.
106 * This function tries each approach in reverse-date order.
107 *
108 * @since 3.0.0
109 * @since 3.5.0 Moved from Core\Utils to Component\Post\PostUtils
110 * @since 4.0.0 Renamed to getContentId() & prioritise beyondwords_content_id
111 *
112 * @param int $postId Post ID.
113 *
114 * @return int|false Content ID, or false
115 */
116 public static function getContentId($postId)
117 {
118 // Check for "Content ID" custom field (string, uuid)
119 $contentId = get_post_meta($postId, 'beyondwords_content_id', true);
120
121 if (! $contentId) {
122 // Also try "Podcast ID" custom field (number, or string for > 4.x)
123 $contentId = PostMetaUtils::getPodcastId($postId);
124 }
125
126 /**
127 * Filters the BeyondWords Content ID.
128 *
129 * @since 4.0.0
130 *
131 * @param string $contentId The BeyondWords Content ID.
132 * @param int $postId The post ID.
133 */
134 $contentId = apply_filters('beyondwords_content_id', $contentId, $postId);
135
136 return $contentId;
137 }
138
139 /**
140 * Get the (legacy) Podcast ID for a WordPress Post.
141 *
142 * Over time there have been various approaches to storing the Podcast ID.
143 * This function tries each approach in reverse-date order.
144 *
145 * @since 3.0.0
146 * @since 3.5.0 Moved from Core\Utils to Component\Post\PostUtils
147 * @since 4.0.0 Allow string values for UUIDs stored >= v4.x
148 *
149 * @param int $postId Post ID.
150 *
151 * @return int|false Podcast ID, or false
152 */
153 public static function getPodcastId($postId)
154 {
155 // Check for "Podcast ID" custom field (number, or string for > 4.x)
156 $podcastId = PostMetaUtils::getRenamedPostMeta($postId, 'podcast_id');
157
158 if ($podcastId) {
159 return $podcastId;
160 }
161
162 // It may also be found by parsing post_meta._speechkit_link
163 $speechkit_link = get_post_meta($postId, '_speechkit_link', true);
164 // Player URL can be either /a/[ID] or /e/[ID] or /m/[ID]
165 preg_match('/\/[aem]\/(\d+)/', (string)$speechkit_link, $matches);
166 if ($matches) {
167 return intval($matches[1]);
168 }
169
170 // It may also be found by parsing post_meta.speechkit_response
171 $speechkit_response = static::getHttpResponseBodyFromPostMeta($postId, 'speechkit_response');
172 preg_match('/"podcast_id":(")?(\d+)(?(1)\1|)/', (string)$speechkit_response, $matches);
173 // $matches[2] is the Podcast ID (response.podcast_id)
174 if ($matches && $matches[2]) {
175 return intval($matches[2]);
176 }
177
178 /**
179 * It may also be found by parsing post_meta.speechkit_info
180 *
181 * NOTE: This has been copied verbatim from the existing iframe player check
182 * at Speechkit_Public::iframe_player_embed_html(), in case it is
183 * needed for posts which were created a very long time ago.
184 * I cannot write unit tests for this to pass, they always fail for me,
185 * so there are currently no tests for it.
186 **/
187 $article = get_post_meta($postId, 'speechkit_info', true);
188 if (empty($article) || ! isset($article['share_url'])) {
189 // This is exactly the same if/else statement that we have at
190 // Speechkit_Public::iframe_player_embed_html(), but there is
191 // nothing for us to to do here.
192 } else {
193 // This is the part that we need...
194 $url = $article['share_url'];
195
196 // Player URL can be either /a/[ID] or /e/[ID] or /m/[ID]
197 preg_match('/\/[aem]\/(\d+)/', (string)$url, $matches);
198 if ($matches) {
199 return intval($matches[1]);
200 }
201 }
202
203 // todo throw ContentIdNotFoundException???
204 return false;
205 }
206
207 /**
208 * Get the 'Generate Audio' value for a Post.
209 *
210 * @since 3.0.0
211 * @since 3.5.0 Moved from Core\Utils to Component\Post\PostUtils
212 *
213 * @param int $postId Post ID.
214 *
215 * @return bool
216 */
217 public static function hasGenerateAudio($postId)
218 {
219 if (PostMetaUtils::getRenamedPostMeta($postId, 'generate_audio') === '1') {
220 return true;
221 }
222
223 if (get_post_meta($postId, 'publish_post_to_speechkit', true) === '1') {
224 return true;
225 }
226
227 $projectId = PostMetaUtils::getProjectId($postId);
228 $contentId = PostMetaUtils::getContentId($postId);
229
230 if ($projectId && $contentId) {
231 return true;
232 }
233
234 return false;
235 }
236
237 /**
238 * Get the Project ID for a WordPress Post.
239 *
240 * It is possible to change the BeyondWords project ID in the plugin settings,
241 * so the current Project ID setting will not necessarily be correct for all
242 * historic Posts. Because of this, we attempt to retrive the correct Project ID
243 * from various custom fields, then fall-back to the plugin setting.
244 *
245 * @since 3.0.0
246 * @since 3.5.0 Moved from Core\Utils to Component\Post\PostUtils
247 * @since 4.0.0 Apply beyondwords_project_id filter
248 *
249 * @param int $postId Post ID.
250 *
251 * @return int|false Project ID, or false
252 */
253 public static function getProjectId($postId)
254 {
255 // Check custom fields
256 $projectId = intval(PostMetaUtils::getRenamedPostMeta($postId, 'project_id'));
257
258 if (! $projectId) {
259 // It may also be found by parsing post_meta.speechkit_response
260 $speechkit_response = static::getHttpResponseBodyFromPostMeta($postId, 'speechkit_response');
261 preg_match('/"project_id":(")?(\d+)(?(1)\1|)/', (string)$speechkit_response, $matches);
262 // $matches[2] is the Project ID (response.project_id)
263 if ($matches && $matches[2]) {
264 $projectId = intval($matches[2]);
265 }
266 }
267
268 // If we still don't have an ID then use the default from the plugin settings
269 if (! $projectId) {
270 $projectId = intval(get_option('beyondwords_project_id'));
271 }
272
273 if (! $projectId) {
274 // Return boolean false instead of numeric 0
275 $projectId = false;
276 }
277
278 // todo throw ProjectIdNotFoundException?
279
280 /**
281 * Filters the BeyondWords Project ID.
282 *
283 * @since 4.0.0
284 *
285 * @param string $contentId The BeyondWords Project ID.
286 * @param int $postId The post ID.
287 */
288 $projectId = apply_filters('beyondwords_project_id', $projectId, $postId);
289
290 return $projectId;
291 }
292
293 /**
294 * Get the Body Voice ID for a WordPress Post.
295 *
296 * We do not filter this, because the Block Editor directly accesses this
297 * custom field, bypassing any filters we add here.
298 *
299 * @since 4.0.0
300 *
301 * @param int $postId Post ID.
302 *
303 * @return int|false Body Voice ID, or false
304 */
305 public static function getBodyVoiceId($postId)
306 {
307 $voiceId = get_post_meta($postId, 'beyondwords_body_voice_id', true);
308
309 return $voiceId;
310 }
311
312 /**
313 * Get the Title Voice ID for a WordPress Post.
314 *
315 * We do not filter this, because the Block Editor directly accesses this
316 * custom field, bypassing any filters we add here.
317 *
318 * @since 4.0.0
319 *
320 * @param int $postId Post ID.
321 *
322 * @return int|false Title Voice ID, or false
323 */
324 public static function getTitleVoiceId($postId)
325 {
326 $voiceId = get_post_meta($postId, 'beyondwords_title_voice_id', true);
327
328 return $voiceId;
329 }
330
331 /**
332 * Get the Summary Voice ID for a WordPress Post.
333 *
334 * We do not filter this, because the Block Editor directly accesses this
335 * custom field, bypassing any filters we add here.
336 *
337 * @since 4.0.0
338 *
339 * @param int $postId Post ID.
340 *
341 * @return int|false Summary Voice ID, or false
342 */
343 public static function getSummaryVoiceId($postId)
344 {
345 $voiceId = get_post_meta($postId, 'beyondwords_summary_voice_id', true);
346
347 return $voiceId;
348 }
349
350 /**
351 * Get the "Error Message" value for a WordPress Post.
352 *
353 * Supports data saved with the `beyondwords_*` prefix and the legacy `speechkit_*` prefix.
354 *
355 * @since 3.7.0
356 *
357 * @param int $postId Post ID.
358 *
359 * @return string
360 */
361 public static function getErrorMessage($postId)
362 {
363 return PostMetaUtils::getRenamedPostMeta($postId, 'error_message');
364 }
365
366 /**
367 * Get the "Disabled" value for a WordPress Post.
368 *
369 * Supports data saved with the `beyondwords_*` prefix and the legacy `speechkit_*` prefix.
370 *
371 * @since 3.7.0
372 *
373 * @param int $postId Post ID.
374 *
375 * @return string
376 */
377 public static function getDisabled($postId)
378 {
379 return PostMetaUtils::getRenamedPostMeta($postId, 'disabled');
380 }
381
382 /**
383 * Get HTTP response body from post meta.
384 *
385 * The data may have been saved as a WordPress HTTP response array. If it was,
386 * then return the 'body' key of the HTTP response instead of the raw post meta.
387 *
388 * The data may also have been saved as a WordPress WP_Error instance. If it was,
389 * then return a string containing the WP_Error code and message.
390 *
391 * @since 3.0.3
392 * @since 3.5.0 Moved from Core\Utils to Component\Post\PostUtils
393 * @since 3.6.1 Handle responses saved as object of class WP_Error
394 *
395 * @param int $postId Post ID.
396 * @param string $metaName Post Meta name.
397 *
398 * @return string
399 */
400 public static function getHttpResponseBodyFromPostMeta($postId, $metaName)
401 {
402 $postMeta = get_post_meta($postId, $metaName, true);
403
404 if (is_array($postMeta)) {
405 return (string)wp_remote_retrieve_body($postMeta);
406 }
407
408 if (is_wp_error($postMeta)) {
409 return sprintf(PostMetaUtils::WP_ERROR_FORMAT, $postMeta->get_error_code(), $postMeta->get_error_message());
410 }
411
412 return (string)$postMeta;
413 }
414 }
415