|
7 | 7 |
|
8 | 8 | use Classifai\Providers\Provider;
|
9 | 9 | use Classifai\Features\TextToSpeech as FeatureTextToSpeech;
|
| 10 | +use WP_Error; |
10 | 11 |
|
11 | 12 | class TextToSpeech extends Provider {
|
12 | 13 | use OpenAI;
|
13 | 14 |
|
14 |
| - const ID = 'openai_text-to-speech'; |
| 15 | + const ID = 'openai_text_to_speech'; |
15 | 16 |
|
16 | 17 | /**
|
17 | 18 | * OpenAI Text to Speech URL.
|
@@ -209,4 +210,112 @@ public function sanitize_settings( array $new_settings ): array {
|
209 | 210 |
|
210 | 211 | return $new_settings;
|
211 | 212 | }
|
| 213 | + |
| 214 | + /** |
| 215 | + * Common entry point for all REST endpoints for this provider. |
| 216 | + * |
| 217 | + * @param int $post_id The post ID we're processing. |
| 218 | + * @param string $route_to_call The name of the route we're going to be processing. |
| 219 | + * @param array $args Optional arguments to pass to the route. |
| 220 | + * @return array|string|WP_Error |
| 221 | + */ |
| 222 | + public function rest_endpoint_callback( $post_id, string $route_to_call = '', array $args = [] ) { |
| 223 | + if ( ! $post_id || ! get_post( $post_id ) ) { |
| 224 | + return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required.', 'classifai' ) ); |
| 225 | + } |
| 226 | + |
| 227 | + $route_to_call = strtolower( $route_to_call ); |
| 228 | + $return = ''; |
| 229 | + |
| 230 | + // Handle all of our routes. |
| 231 | + switch ( $route_to_call ) { |
| 232 | + case 'synthesize': |
| 233 | + $return = $this->synthesize_speech( $post_id, $args ); |
| 234 | + break; |
| 235 | + } |
| 236 | + |
| 237 | + return $return; |
| 238 | + } |
| 239 | + |
| 240 | + /** |
| 241 | + * Synthesizes speech from a post item. |
| 242 | + * |
| 243 | + * @param int $post_id Post ID. |
| 244 | + * @return string|WP_Error |
| 245 | + */ |
| 246 | + public function synthesize_speech( int $post_id ) { |
| 247 | + if ( empty( $post_id ) ) { |
| 248 | + return new WP_Error( |
| 249 | + 'openai_text_to_speech_post_id_missing', |
| 250 | + esc_html__( 'Post ID missing.', 'classifai' ) |
| 251 | + ); |
| 252 | + } |
| 253 | + |
| 254 | + // We skip the user cap check if running under WP-CLI. |
| 255 | + if ( ! current_user_can( 'edit_post', $post_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { |
| 256 | + return new WP_Error( |
| 257 | + 'openai_text_to_speech_user_not_authorized', |
| 258 | + esc_html__( 'Unauthorized user.', 'classifai' ) |
| 259 | + ); |
| 260 | + } |
| 261 | + |
| 262 | + $feature = new FeatureTextToSpeech(); |
| 263 | + $settings = $feature->get_settings(); |
| 264 | + $post_content = $feature->normalize_post_content( $post_id ); |
| 265 | + $content_hash = get_post_meta( $post_id, FeatureTextToSpeech::AUDIO_HASH_KEY, true ); |
| 266 | + $saved_attachment_id = (int) get_post_meta( $post_id, $feature::AUDIO_ID_KEY, true ); |
| 267 | + |
| 268 | + // Don't regenerate the audio file it it already exists and the content hasn't changed. |
| 269 | + if ( $saved_attachment_id ) { |
| 270 | + |
| 271 | + // Check if the audio file exists. |
| 272 | + $audio_attachment_url = wp_get_attachment_url( $saved_attachment_id ); |
| 273 | + |
| 274 | + if ( $audio_attachment_url && ! empty( $content_hash ) && ( md5( $post_content ) === $content_hash ) ) { |
| 275 | + return $saved_attachment_id; |
| 276 | + } |
| 277 | + } |
| 278 | + |
| 279 | + // Create the request body to synthesize speech from text. |
| 280 | + $request_body = array( |
| 281 | + 'model' => $settings[ static::ID ]['tts_model'], |
| 282 | + 'voice' => $settings[ static::ID ]['voice'], |
| 283 | + 'input' => $post_content, |
| 284 | + ); |
| 285 | + |
| 286 | + // Request parameters. |
| 287 | + $request_params = array( |
| 288 | + 'method' => 'POST', |
| 289 | + 'body' => wp_json_encode( $request_body ), |
| 290 | + 'timeout' => 60, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout |
| 291 | + 'headers' => array( |
| 292 | + 'Authorization' => 'Bearer ' . $settings[ static::ID ]['api_key'], |
| 293 | + 'Content-Type' => 'application/json', |
| 294 | + ), |
| 295 | + ); |
| 296 | + |
| 297 | + $response = wp_remote_post( $this->api_url, $request_params ); |
| 298 | + |
| 299 | + if ( is_wp_error( $response ) ) { |
| 300 | + return new WP_Error( |
| 301 | + 'openai_text_to_speech_http_error', |
| 302 | + esc_html( $response->get_error_message() ) |
| 303 | + ); |
| 304 | + } |
| 305 | + |
| 306 | + $code = wp_remote_retrieve_response_code( $response ); |
| 307 | + $response_body = wp_remote_retrieve_body( $response ); |
| 308 | + |
| 309 | + // return error if HTTP status code is not 200. |
| 310 | + if ( \WP_Http::OK !== $code ) { |
| 311 | + return new WP_Error( |
| 312 | + 'openai_text_to_speech_unsuccessful_request', |
| 313 | + esc_html__( 'HTTP request unsuccessful.', 'classifai' ) |
| 314 | + ); |
| 315 | + } |
| 316 | + |
| 317 | + update_post_meta( $post_id, FeatureTextToSpeech::AUDIO_HASH_KEY, md5( $post_content ) ); |
| 318 | + |
| 319 | + return $response_body; |
| 320 | + } |
212 | 321 | }
|
0 commit comments