PluginProbe
BeyondWords – AI audio for publishers / 4.0.0
BeyondWords – AI audio for publishers v4.0.0
7.2.0 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 All 44 releases
speechkit / src / Core / ApiClient.php

ApiClient.php in BeyondWords – AI audio for publishers 4.0.0, at src/Core/ApiClient.php

455 lines 12.5 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\Core\Environment;
8 use Beyondwords\Wordpress\Core\Request;
9 use Beyondwords\Wordpress\Component\Post\PostContentUtils;
10 use Beyondwords\Wordpress\Component\Post\PostMetaUtils;
11 use Beyondwords\Wordpress\Component\Settings\SettingsUtils;
12
13 class ApiClient
14 {
15 public const ERROR_FORMAT = '#%s: %s';
16
17 public $errors;
18
19 /**
20 * Constructor
21 *
22 * @since 3.0.0
23 */
24 public function __construct()
25 {
26 add_action('admin_notices', array($this, 'adminNotices'));
27
28 $this->errors = [];
29 }
30
31 /**
32 * POST /projects/:id/content.
33 *
34 * @since 3.0.0
35 *
36 * @param $postId WordPress Post ID
37 *
38 * @return Response|false Response, or false
39 **/
40 public function createAudio($postId)
41 {
42 $projectId = PostMetaUtils::getProjectId($postId);
43
44 $url = sprintf('%s/projects/%d/content', Environment::getApiUrl(), $projectId);
45
46 $body = PostContentUtils::getBodyJson($postId);
47
48 $request = new Request('POST', $url, $body);
49
50 return $this->callApi($postId, $request);
51 }
52
53 /**
54 * PUT /projects/:id/content/:id.
55 *
56 * @since 3.0.0
57 *
58 * @param $postId WordPress Post ID
59 *
60 * @return Response|false Response, or false
61 **/
62 public function updateAudio($postId)
63 {
64 $projectId = PostMetaUtils::getProjectId($postId);
65 $contentId = PostMetaUtils::getContentId($postId);
66
67 $url = sprintf('%s/projects/%d/content/%s', Environment::getApiUrl(), $projectId, $contentId);
68
69 $body = PostContentUtils::getBodyJson($postId);
70
71 $request = new Request('PUT', $url, $body);
72
73 return $this->callApi($postId, $request);
74 }
75
76 /**
77 * DELETE /projects/:id/content/:id.
78 *
79 * @since 3.0.0
80 *
81 * @param $postId WordPress Post ID
82 *
83 * @return Response|false Response, or false
84 **/
85 public function deleteAudio($postId)
86 {
87 $projectId = PostMetaUtils::getProjectId($postId);
88 $contentId = PostMetaUtils::getContentId($postId);
89
90 $url = sprintf('%s/projects/%d/content/%s', Environment::getApiUrl(), $projectId, $contentId);
91
92 $request = new Request('DELETE', $url);
93
94 return $this->callApi($postId, $request);
95 }
96
97 /**
98 * GET /languages
99 *
100 * @since 4.0.0
101 *
102 * @return array|object Array of voices or API error object.
103 **/
104 public function getLanguages()
105 {
106 $url = sprintf('%s/languages', Environment::getApiUrl());
107
108 $request = new Request('GET', $url);
109
110 $args = array(
111 'blocking' => true,
112 'headers' => $request->getHeaders(),
113 'method' => $request->getMethod(),
114 'sslverify' => true,
115 );
116
117 $response = wp_remote_request($request->getUrl(), $args);
118
119 // WordPress error performing API call
120 if (is_wp_error($response) && get_the_ID()) {
121 return $this->error(
122 get_the_ID(),
123 $response->get_error_message(),
124 $response->get_error_code()
125 );
126 }
127
128 $responseBody = wp_remote_retrieve_body($response);
129
130 return json_decode($responseBody, true);
131 }
132
133 /**
134 * GET /voices
135 *
136 * @since 4.0.0
137 *
138 * @param $languageId BeyondWords Language ID
139 *
140 * @return array|object Array of voices or API error object.
141 **/
142 public function getVoices($languageId)
143 {
144 $url = sprintf('%s/voices?language_id=%d', Environment::getApiUrl(), urlencode($languageId));
145
146 $request = new Request('GET', $url);
147
148 $args = array(
149 'blocking' => true,
150 'headers' => $request->getHeaders(),
151 'method' => $request->getMethod(),
152 'sslverify' => true,
153 );
154
155 $response = wp_remote_request($request->getUrl(), $args);
156
157 // WordPress error performing API call
158 if (is_wp_error($response) && get_the_ID()) {
159 return $this->error(
160 get_the_ID(),
161 $response->get_error_message(),
162 $response->get_error_code()
163 );
164 }
165
166 $responseBody = wp_remote_retrieve_body($response);
167
168 return json_decode($responseBody, true);
169 }
170
171 /**
172 * GET /projects/:id.
173 *
174 * @since 4.0.0
175 *
176 * @return Response|false Response, or false
177 **/
178 public function getProject()
179 {
180 $projectId = get_option('beyondwords_project_id');
181
182 if (! $projectId) {
183 return false;
184 }
185
186 $url = sprintf('%s/projects/%d', Environment::getApiUrl(), $projectId);
187
188 $request = new Request('GET', $url);
189
190 $args = array(
191 'blocking' => true,
192 'headers' => $request->getHeaders(),
193 'method' => $request->getMethod(),
194 'sslverify' => true,
195 );
196
197 $response = wp_remote_request($request->getUrl(), $args);
198
199 // WordPress error performing API call
200 if (is_wp_error($response) && get_the_ID()) {
201 return $this->error(
202 get_the_ID(),
203 $response->get_error_message(),
204 $response->get_error_code()
205 );
206 }
207
208 $responseBody = wp_remote_retrieve_body($response);
209
210 return json_decode($responseBody, true);
211 }
212
213 /**
214 * GET /projects/:id/player_settings.
215 *
216 * @since 4.0.0
217 *
218 * @param array $settings Associative array if player settings.
219 *
220 * @return Response|false Response, or false
221 **/
222 public function getPlayerSettings()
223 {
224 $projectId = get_option('beyondwords_project_id');
225
226 if (! $projectId) {
227 return false;
228 }
229
230 $url = sprintf('%s/projects/%d/player_settings', Environment::getApiUrl(), $projectId);
231
232 $request = new Request('GET', $url);
233
234 $args = array(
235 'blocking' => true,
236 'headers' => $request->getHeaders(),
237 'method' => $request->getMethod(),
238 'sslverify' => true,
239 );
240
241 $response = wp_remote_request($request->getUrl(), $args);
242
243 // WordPress error performing API call
244 if (is_wp_error($response) && get_the_ID()) {
245 return $this->error(
246 get_the_ID(),
247 $response->get_error_message(),
248 $response->get_error_code()
249 );
250 }
251
252 $responseBody = wp_remote_retrieve_body($response);
253
254 return json_decode($responseBody, true);
255 }
256
257 /**
258 * PUT /projects/:id/player_settings.
259 *
260 * @since 4.0.0
261 *
262 * @param array $settings Associative array if player settings.
263 *
264 * @return Response|false Response, or false
265 **/
266 public function updatePlayerSettings($settings)
267 {
268 $projectId = get_option('beyondwords_project_id');
269
270 if (! $projectId) {
271 return false;
272 }
273
274 $url = sprintf('%s/projects/%d/player_settings', Environment::getApiUrl(), $projectId);
275
276 $request = new Request('PUT', $url, $settings);
277
278 $args = array(
279 'blocking' => true,
280 'body' => wp_json_encode($settings),
281 'headers' => $request->getHeaders(),
282 'method' => $request->getMethod(),
283 'sslverify' => true,
284 );
285
286 $response = wp_remote_request($request->getUrl(), $args);
287
288 // WordPress error performing API call
289 if (is_wp_error($response) && get_the_ID()) {
290 return $this->error(
291 get_the_ID(),
292 $response->get_error_message(),
293 $response->get_error_code()
294 );
295 }
296
297 $responseBody = wp_remote_retrieve_body($response);
298
299 return json_decode($responseBody, true);
300 }
301
302 /**
303 * Call the BeyondWords API backend.
304 *
305 * @since 3.0.0
306 * @since 3.9.0 Stop saving the speechkit_status post meta - downgrades to plugin v2.x are no longer expected.
307 * @since 4.0.0 Removed hash comparison.
308 *
309 * @param int $postId Post ID.
310 * @param Request $request Request.
311 *
312 * @return array|false JSON-decoded response body, or false on failure
313 **/
314 public function callApi($postId, $request)
315 {
316 $args = array(
317 'blocking' => true,
318 'body' => $request->getBody(),
319 'headers' => $request->getHeaders(),
320 'method' => $request->getMethod(),
321 'sslverify' => true,
322 );
323
324 // Reset any existing errors before making this API call
325 delete_post_meta($postId, 'speechkit_error_message');
326 delete_post_meta($postId, 'beyondwords_error_message');
327
328 // Log the request URL and args for debugging
329 // (these are removed for successful requests)
330 update_post_meta($postId, 'beyondwords_request_url', $request->getUrl());
331 update_post_meta($postId, 'beyondwords_request_args', var_export($args, true)); // phpcs:ignore
332
333 $response = wp_remote_request($request->getUrl(), $args);
334
335 // WordPress error performing API call
336 if (is_wp_error($response)) {
337 $errorMessage = $response->get_error_message();
338
339 return $this->error($postId, $errorMessage, $response->get_error_code());
340 }
341
342 $responseCode = wp_remote_retrieve_response_code($response);
343
344 $responseBody = wp_remote_retrieve_body($response);
345 $responseBody = json_decode($responseBody, true);
346
347 // Response had a HTTP error code (3XX, 4XX, 5XX)
348 if ($responseCode > 299) {
349 if (is_array($responseBody) && array_key_exists('message', $responseBody)) {
350 $errorMessage = $responseBody['message'];
351 } else {
352 $errorMessage = wp_remote_retrieve_response_message($response);
353 }
354
355 if (! $errorMessage) {
356 $errorMessage = sprintf(
357 /* translators: %s is replaced with the support email link */
358 esc_html__('API request error. Please contact %s.', 'speechkit'),
359 '<a href="mailto:[email protected]">[email protected]</a>'
360 );
361 }
362
363 return $this->error($postId, $errorMessage, $responseCode);
364 }
365
366 // Response was invalid JSON
367 if (json_last_error() !== JSON_ERROR_NONE) {
368 $errorMessage = sprintf(
369 /* translators: %s is replaced with the reason that JSON parsing failed */
370 __('Unable to parse JSON in BeyondWords API response. Reason: %s.', 'speechkit'),
371 // Don't allow any tags
372 wp_kses(json_last_error_msg(), [])
373 );
374
375 return $this->error($postId, $errorMessage, 500);
376 }
377
378 // Success, so remove request URL and args log
379 delete_post_meta($postId, 'beyondwords_request_url');
380 delete_post_meta($postId, 'beyondwords_request_args');
381
382 return $responseBody;
383 }
384
385 /**
386 * Handle API Error.
387 *
388 * @since 3.0.0
389 * @since 4.0.0 Removed hash comparison and display 403 errors.
390 *
391 * @param int $postId Post ID.
392 * @param string $message Error Message.
393 * @param int $code Error Code.
394 *
395 * @throws \Exception
396 */
397 public function error($postId, $message, $code = 0)
398 {
399 $error = sprintf(self::ERROR_FORMAT, $code, $message);
400
401 // Log the error message for this Post in the db
402 update_post_meta($postId, 'beyondwords_error_message', $error);
403
404 return false;
405 }
406
407 /**
408 * Get error message.
409 *
410 * @since 3.0.0
411 *
412 * @param error
413 *
414 * @return string
415 */
416 public function getErrorMessage($error)
417 {
418 return $error['message'];
419 }
420
421 /**
422 * Print admin notices.
423 *
424 * @since 3.0.0
425 *
426 * @return void
427 */
428 public function adminNotices()
429 {
430 $screen = get_current_screen();
431
432 // Only add for enabled Posts screen
433 $postTypes = SettingsUtils::getSupportedPostTypes();
434
435 if (! in_array($screen->id, $postTypes)) {
436 return;
437 }
438
439 $errorMessage = PostMetaUtils::getErrorMessage(get_the_ID());
440
441 if (!$errorMessage) {
442 return;
443 }
444
445 ?>
446 <div class="notice notice-error">
447 <p>
448 <span class="dashicons dashicons-controls-volumeon"></span>
449 <?php echo esc_html($errorMessage); ?>
450 </p>
451 </div>
452 <?php
453 }
454 }
455