Skip to content

Commit c6c51e7

Browse files
authored
Merge pull request #1042 from 10up/feature/tts-bkg-schedule
Move text-to-speech audio generation to Action Scheduler
2 parents 945ec01 + 221e6b6 commit c6c51e7

File tree

8 files changed

+325
-58
lines changed

8 files changed

+325
-58
lines changed

includes/Classifai/Features/Classification.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,14 @@ public function register_endpoints() {
9595
$post_types = $this->get_supported_post_types();
9696
foreach ( $post_types as $post_type ) {
9797
register_meta(
98-
$post_type,
98+
'post',
9999
'_classifai_error',
100100
[
101-
'show_in_rest' => true,
102-
'single' => true,
103-
'auth_callback' => '__return_true',
101+
'object_subtype' => $post_type,
102+
'type' => 'string',
103+
'show_in_rest' => true,
104+
'single' => true,
105+
'auth_callback' => '__return_true',
104106
]
105107
);
106108
}

includes/Classifai/Features/TextToSpeech.php

Lines changed: 125 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ public function __construct() {
7878
}
7979

8080
/**
81-
* Set up necessary hooks.
82-
*
83-
* We utilize this so we can register the REST route.
81+
* Set up necessary hooks even if the Feature is not available.
8482
*/
8583
public function setup() {
8684
parent::setup();
85+
8786
add_action( 'rest_api_init', [ $this, 'register_endpoints' ] );
87+
add_action( 'classifai_schedule_text_to_speech_job', [ $this, 'generate_text_to_speech_audio' ], 10, 2 );
8888

8989
if ( $this->is_enabled() ) {
9090
add_filter( 'the_content', [ $this, 'render_post_audio_controls' ] );
@@ -218,11 +218,28 @@ public function add_meta_to_rest_api() {
218218
* @param WP_REST_Request $request Request object.
219219
*/
220220
public function rest_handle_audio( \WP_Post $post, WP_REST_Request $request ) {
221+
$post_id = (int) $request->get_param( 'id' );
222+
221223
if ( ! $this->is_feature_enabled() ) {
222224
return;
223225
}
224226

225-
$audio_id = get_post_meta( $request->get_param( 'id' ), self::AUDIO_ID_KEY, true );
227+
// Ensure we have a logged in user that can edit the item.
228+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
229+
return;
230+
}
231+
232+
$job_args = [
233+
'post_id' => $post_id,
234+
'calling_user_id' => get_current_user_id(),
235+
];
236+
237+
// We return early if the job is already scheduled.
238+
if ( function_exists( 'as_has_scheduled_action' ) && \as_has_scheduled_action( 'classifai_schedule_text_to_speech_job', $job_args, 'classifai' ) ) {
239+
return;
240+
}
241+
242+
$audio_id = get_post_meta( $post_id, self::AUDIO_ID_KEY, true );
226243

227244
// Since we have dynamic generation option agnostic to meta saves we need a flag to differentiate audio generation accurately
228245
$process_content = false;
@@ -238,39 +255,42 @@ public function rest_handle_audio( \WP_Post $post, WP_REST_Request $request ) {
238255
( $process_content && null === $request->get_param( 'classifai_synthesize_speech' ) ) ||
239256
true === $request->get_param( 'classifai_synthesize_speech' )
240257
) {
241-
$results = $this->run( $request->get_param( 'id' ), 'synthesize' );
242-
243-
if ( $results && ! is_wp_error( $results ) ) {
244-
$this->save( $results, $request->get_param( 'id' ) );
245-
delete_post_meta( $post->ID, '_classifai_text_to_speech_error' );
246-
} elseif ( is_wp_error( $results ) ) {
247-
update_post_meta(
248-
$post->ID,
249-
'_classifai_text_to_speech_error',
250-
wp_json_encode(
251-
[
252-
'code' => $results->get_error_code(),
253-
'message' => $results->get_error_message(),
254-
]
255-
)
256-
);
258+
if ( function_exists( 'as_enqueue_async_action' ) ) {
259+
\as_enqueue_async_action( 'classifai_schedule_text_to_speech_job', $job_args, 'classifai' );
260+
update_post_meta( $post_id, '_classifai_text_to_speech_scheduled', true );
261+
} else {
262+
$this->generate_text_to_speech_audio( $post_id );
257263
}
258264
}
259265
}
260266

261267
/**
262-
* Register any needed endpoints.
268+
* Register any needed endpoints and meta.
263269
*/
264270
public function register_endpoints() {
265271
$post_types = $this->get_supported_post_types();
266272
foreach ( $post_types as $post_type ) {
267273
register_meta(
268-
$post_type,
274+
'post',
269275
'_classifai_text_to_speech_error',
270276
[
271-
'show_in_rest' => true,
272-
'single' => true,
273-
'auth_callback' => '__return_true',
277+
'object_subtype' => $post_type,
278+
'type' => 'string',
279+
'show_in_rest' => true,
280+
'single' => true,
281+
'auth_callback' => '__return_true',
282+
]
283+
);
284+
285+
register_meta(
286+
'post',
287+
'_classifai_text_to_speech_scheduled',
288+
[
289+
'object_subtype' => $post_type,
290+
'type' => 'boolean',
291+
'show_in_rest' => true,
292+
'single' => true,
293+
'auth_callback' => '__return_true',
274294
]
275295
);
276296
}
@@ -425,7 +445,23 @@ public function render_meta_box( \WP_Post $post ) {
425445
if ( $post_type ) {
426446
$post_type_label = $post_type->labels->singular_name;
427447
}
428-
?>
448+
449+
$is_as_scheduled_job =
450+
function_exists( 'as_has_scheduled_action' ) &&
451+
\as_has_scheduled_action(
452+
'classifai_schedule_text_to_speech_job',
453+
[
454+
'post_id' => (int) $post->ID,
455+
'calling_user_id' => get_current_user_id(),
456+
],
457+
'classifai'
458+
);
459+
460+
if ( $is_as_scheduled_job ) : ?>
461+
<p>
462+
<?php esc_html_e( 'Audio generation is in progress…', 'classifai' ); ?>
463+
</p>
464+
<?php else : ?>
429465

430466
<p>
431467
<label for="classifai_synthesize_speech">
@@ -452,6 +488,8 @@ public function render_meta_box( \WP_Post $post ) {
452488
</span>
453489
</p>
454490

491+
<?php endif; ?>
492+
455493
<?php
456494
if ( $source_url ) {
457495
$cache_busting_url = add_query_arg(
@@ -497,27 +535,71 @@ public function save_post_metadata( int $post_id ) {
497535
delete_post_meta( $post_id, self::DISPLAY_GENERATED_AUDIO );
498536
}
499537

500-
if ( isset( $_POST['classifai_synthesize_speech'] ) ) {
501-
$results = $this->run( $post_id, 'synthesize' );
538+
$job_args = [
539+
'post_id' => (int) $post_id,
540+
'calling_user_id' => get_current_user_id(),
541+
];
502542

503-
if ( $results && ! is_wp_error( $results ) ) {
504-
$this->save( $results, $post_id );
505-
delete_post_meta( $post_id, '_classifai_text_to_speech_error' );
506-
} elseif ( is_wp_error( $results ) ) {
507-
update_post_meta(
508-
$post_id,
509-
'_classifai_text_to_speech_error',
510-
wp_json_encode(
511-
[
512-
'code' => $results->get_error_code(),
513-
'message' => $results->get_error_message(),
514-
]
515-
)
516-
);
517-
}
543+
// We return early if the job is already scheduled.
544+
if (
545+
! isset( $_POST['classifai_synthesize_speech'] )
546+
|| (
547+
function_exists( 'as_has_scheduled_action' )
548+
&& \as_has_scheduled_action( 'classifai_schedule_text_to_speech_job', $job_args, 'classifai' )
549+
)
550+
) {
551+
return;
552+
}
553+
554+
// We enqueue the async action to generate the audio, if available.
555+
if ( function_exists( 'as_enqueue_async_action' ) ) {
556+
\as_enqueue_async_action( 'classifai_schedule_text_to_speech_job', $job_args, 'classifai' );
557+
update_post_meta( $post_id, '_classifai_text_to_speech_scheduled', true );
558+
} else {
559+
$this->generate_text_to_speech_audio( $post_id );
518560
}
519561
}
520562

563+
/**
564+
* Generate the text to speech audio.
565+
*
566+
* @param int $post_id The post ID.
567+
* @param int|null $calling_user_id The user that made the request.
568+
*/
569+
public function generate_text_to_speech_audio( int $post_id, ?int $calling_user_id = null ) {
570+
$original_user_id = get_current_user_id();
571+
572+
if ( ! $calling_user_id ) {
573+
$calling_user_id = $original_user_id;
574+
}
575+
576+
// Set the user to the one who started the process, to avoid permission issues.
577+
wp_set_current_user( (int) $calling_user_id );
578+
579+
$results = $this->run( $post_id, 'synthesize' );
580+
581+
if ( $results && ! is_wp_error( $results ) ) {
582+
$this->save( $results, $post_id );
583+
delete_post_meta( $post_id, '_classifai_text_to_speech_error' );
584+
} elseif ( is_wp_error( $results ) ) {
585+
update_post_meta(
586+
$post_id,
587+
'_classifai_text_to_speech_error',
588+
wp_json_encode(
589+
[
590+
'code' => $results->get_error_code(),
591+
'message' => $results->get_error_message(),
592+
]
593+
)
594+
);
595+
}
596+
597+
// Restore original user.
598+
wp_set_current_user( $original_user_id );
599+
600+
delete_post_meta( $post_id, '_classifai_text_to_speech_scheduled' );
601+
}
602+
521603
/**
522604
* Save the returned result.
523605
*

includes/Classifai/Plugin.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,13 @@ public function load_action_scheduler() {
314314
new \Classifai\Features\Classification(),
315315
new \Classifai\Features\TermCleanup(),
316316
new \Classifai\Features\RecommendedContent(),
317+
new \Classifai\Features\TextToSpeech(),
317318
];
318319
$is_feature_being_enabled = false;
319320

320321
foreach ( $features as $feature ) {
321322
if ( ! $feature->get_feature_provider_instance() ) {
322-
// Skip if the feature does not have a provider instance.
323+
// Skip if the feature does not have a Provider instance.
323324
continue;
324325
}
325326

@@ -328,6 +329,10 @@ public function load_action_scheduler() {
328329
case 'openai_embeddings':
329330
case 'azure_openai_embeddings':
330331
case 'ollama_embeddings':
332+
case 'aws_polly':
333+
case 'ms_azure_text_to_speech':
334+
case 'openai_text_to_speech':
335+
case 'elevenlabs_text_to_speech':
331336
break;
332337
default:
333338
continue 2;

0 commit comments

Comments
 (0)