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 / ApiClient.php

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

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