Skip to content

Commit 149c2c8

Browse files
committed
Add support for select voice engine.
1 parent 6e56949 commit 149c2c8

File tree

2 files changed

+159
-10
lines changed

2 files changed

+159
-10
lines changed

includes/Classifai/Providers/AWS/AmazonPolly.php

+112-10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function __construct( $feature_instance = null ) {
3434
$this->feature_instance = $feature_instance;
3535

3636
do_action( 'classifai_' . static::ID . '_init', $this );
37+
add_action( 'wp_ajax_classifai_get_voice_dropdown', [ $this, 'get_voice_dropdown' ] );
3738
}
3839

3940
/**
@@ -107,7 +108,41 @@ public function render_provider_fields() {
107108
]
108109
);
109110

110-
$voices_options = $this->get_voices_select_options();
111+
add_settings_field(
112+
'voice_engine',
113+
esc_html__( 'Engine', 'classifai' ),
114+
[ $this->feature_instance, 'render_select' ],
115+
$this->feature_instance->get_option_name(),
116+
$this->feature_instance->get_option_name() . '_section',
117+
[
118+
'option_index' => static::ID,
119+
'label_for' => 'voice_engine',
120+
'options' => array(
121+
'standard' => esc_html__( 'Standard', 'classifai' ),
122+
'neural' => esc_html__( 'Neural', 'classifai' ),
123+
'long-form' => esc_html__( 'Long Form', 'classifai' ),
124+
),
125+
'default_value' => $settings['voice_engine'],
126+
'class' => 'classifai-provider-field hidden provider-scope-' . static::ID, // Important to add this.
127+
'description' => sprintf(
128+
wp_kses(
129+
/* translators: %1$s is replaced with the OpenAI sign up URL */
130+
__( 'Amazon Polly offers <a href="%1$s">Long-Form</a>, <a href="%2$s">Neural</a> and Standard text-to-speech voices. Please check the <a title="Pricing" href="%3$s">documentation</a> to review pricing for Long-Form, Neural and Standard usage.', 'classifai' ),
131+
[
132+
'a' => [
133+
'href' => [],
134+
'title' => [],
135+
],
136+
]
137+
),
138+
esc_url( 'https://docs.aws.amazon.com/polly/latest/dg/long-form-voice-overview.html' ),
139+
esc_url( 'https://docs.aws.amazon.com/polly/latest/dg/NTTS-main.html' ),
140+
esc_url( 'https://aws.amazon.com/polly/pricing/' )
141+
)
142+
]
143+
);
144+
145+
$voices_options = $this->get_voices_select_options( $settings['voice_engine'] ?? '' );
111146
if ( ! empty( $voices_options ) ) {
112147
add_settings_field(
113148
'voice',
@@ -120,7 +155,7 @@ public function render_provider_fields() {
120155
'label_for' => 'voice',
121156
'options' => $voices_options,
122157
'default_value' => $settings['voice'],
123-
'class' => 'classifai-provider-field hidden provider-scope-' . static::ID, // Important to add this.
158+
'class' => 'classifai-aws-polly-voices classifai-provider-field hidden provider-scope-' . static::ID, // Important to add this.
124159
]
125160
);
126161
}
@@ -253,7 +288,7 @@ public function connect_to_service( array $args = array() ): array {
253288
*
254289
* @return array
255290
*/
256-
public function get_voices_select_options(): array {
291+
public function get_voices_select_options( string $engine = "" ): array {
257292
$settings = $this->feature_instance->get_settings( static::ID );
258293
$voices = $settings['voices'];
259294
$options = array();
@@ -263,7 +298,14 @@ public function get_voices_select_options(): array {
263298
}
264299

265300
foreach ( $voices as $voice ) {
266-
if ( ! is_array( $voice ) || empty( $voice ) ) {
301+
if (
302+
! is_array( $voice ) ||
303+
empty( $voice ) ||
304+
(
305+
! empty( $engine ) &&
306+
! in_array( $engine, $voice['SupportedEngines'], true )
307+
)
308+
) {
267309
continue;
268310
}
269311

@@ -322,15 +364,35 @@ public function synthesize_speech( int $post_id ) {
322364
$voice = $settings[ static::ID ]['voice'] ?? '';
323365

324366
try {
325-
$polly_client = $this->get_polly_client();
326-
$result = $polly_client->synthesizeSpeech(
367+
$polly_client = $this->get_polly_client();
368+
369+
/**
370+
* Filter Synthesize speech args.
371+
*
372+
* @since 3.1.0
373+
* @hook classifai_aws_polly_synthesize_speech_args
374+
*
375+
* @param {array} Associative array of synthesize speech args.
376+
* @param {int} $post_id Post ID.
377+
* @param {object} $provider_instance Provider instance.
378+
* @param {object} $feature_instance Feature instance.
379+
*
380+
* @return {array} The filtered array of synthesize speech args.
381+
*/
382+
$synthesize_data = apply_filters(
383+
'classifai_' . self::ID . '_synthesize_speech_args',
327384
array(
328385
'OutputFormat' => 'mp3',
329386
'Text' => $post_content,
330387
'TextType' => 'text',
388+
'Engine' => $settings[ static::ID ]['voice_engine'] ?? 'standard', // 'standard', 'neural', 'long-form'
331389
'VoiceId' => $voice,
332-
)
390+
),
391+
$post_id,
392+
$this,
393+
$this->feature_instance
333394
);
395+
$result = $polly_client->synthesizeSpeech( $synthesize_data );
334396

335397
update_post_meta( $post_id, self::AUDIO_HASH_KEY, md5( $post_content ) );
336398
$contents = $result['AudioStream']->getContents();
@@ -410,9 +472,9 @@ public function get_polly_client( array $aws_config = array() ) {
410472
$settings = $this->feature_instance->get_settings( static::ID );
411473

412474
$default = array(
413-
'access_key_id' => isset( $settings[ static::ID ]['access_key_id'] ) ? $settings[ static::ID ]['access_key_id'] : '',
414-
'secret_access_key' => isset( $settings[ static::ID ]['secret_access_key'] ) ? $settings[ static::ID ]['secret_access_key'] : '',
415-
'aws_region' => isset( $settings[ static::ID ]['aws_region'] ) ? $settings[ static::ID ]['aws_region'] : 'us-east-1',
475+
'access_key_id' => isset( $settings['access_key_id'] ) ? $settings['access_key_id'] : '',
476+
'secret_access_key' => isset( $settings['secret_access_key'] ) ? $settings['secret_access_key'] : '',
477+
'aws_region' => isset( $settings['aws_region'] ) ? $settings['aws_region'] : 'us-east-1',
416478
);
417479

418480
$default = wp_parse_args( $aws_config, $default );
@@ -436,4 +498,44 @@ public function get_polly_client( array $aws_config = array() ) {
436498
$sdk = new \Aws\Sdk($aws_sdk_config);
437499
return $sdk->createPolly();
438500
}
501+
502+
/**
503+
* Returns the voice dropdown for the selected engine.
504+
*/
505+
public function get_voice_dropdown() {
506+
if ( ! wp_doing_ajax() ) {
507+
return;
508+
}
509+
510+
// Nonce check.
511+
if ( ! check_ajax_referer( 'classifai', 'nonce', false ) ) {
512+
$error = new \WP_Error( 'classifai_nonce_error', __( 'Nonce could not be verified.', 'classifai' ) );
513+
wp_send_json_error( $error );
514+
exit();
515+
}
516+
517+
// Set the feature instance if it's not already set.
518+
if ( ! $this->feature_instance instanceof TextToSpeech ) {
519+
$this->feature_instance = new TextToSpeech();
520+
}
521+
522+
// Attachment ID check.
523+
$engine = isset( $_POST['engine'] ) ? sanitize_text_field( wp_unslash( $_POST['engine'] ) ) : 'standard';
524+
$settings = $this->feature_instance->get_settings( static::ID );
525+
$voices_options = $this->get_voices_select_options( $engine );
526+
527+
ob_start();
528+
$this->feature_instance->render_select(
529+
[
530+
'option_index' => static::ID,
531+
'label_for' => 'voice',
532+
'options' => $voices_options,
533+
'default_value' => $settings['voice'],
534+
'class' => 'classifai-provider-field hidden provider-scope-' . static::ID, // Important to add this.
535+
]
536+
);
537+
$voice_dropdown = ob_get_clean();
538+
539+
wp_send_json_success( $voice_dropdown );
540+
}
439541
}

src/js/admin.js

+47
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,50 @@ document.addEventListener( 'DOMContentLoaded', function () {
368368
providerSelectEl.trigger( 'change' );
369369
} );
370370
} )( jQuery );
371+
372+
( function ( $ ) {
373+
$( function () {
374+
const engineSelectEl = $( 'select#voice_engine' );
375+
if ( ! engineSelectEl.length ) {
376+
return;
377+
}
378+
379+
engineSelectEl.on( 'change', function () {
380+
const engine = $( this ).val();
381+
$( 'select#voice' ).prop( 'disabled', true );
382+
$.ajax( {
383+
url: ajaxurl,
384+
type: 'POST',
385+
data: {
386+
action: 'classifai_get_voice_dropdown',
387+
nonce: ClassifAI.ajax_nonce,
388+
engine,
389+
},
390+
success( response ) {
391+
if ( response.success && response.data ) {
392+
jQuery( '.classifai-aws-polly-voices td' ).html(
393+
response.data
394+
);
395+
} else {
396+
// eslint-disable-next-line no-console
397+
console.error( response.data );
398+
}
399+
$( 'select#voice' ).prop( 'disabled', false );
400+
},
401+
error( jqXHR, textStatus, errorThrown ) {
402+
// eslint-disable-next-line no-console
403+
console.error(
404+
'Error: ',
405+
textStatus,
406+
', Details: ',
407+
errorThrown
408+
);
409+
$( 'select#voice' ).prop( 'disabled', false );
410+
},
411+
} );
412+
} );
413+
414+
// Trigger 'change' on page load.
415+
engineSelectEl.trigger( 'change' );
416+
} );
417+
} )( jQuery );

0 commit comments

Comments
 (0)