@@ -105,6 +105,41 @@ public function feature_setup() {
105105 add_action ( 'add_meta_boxes ' , [ $ this , 'add_meta_box ' ] );
106106 add_action ( 'admin_notices ' , [ $ this , 'show_error_if ' ] );
107107 add_action ( 'save_post ' , [ $ this , 'save_post_metadata ' ], 5 );
108+ add_action ( 'wp_ajax_classifai_get_tts_status ' , [ $ this , 'ajax_get_audio_generation_status ' ] );
109+ add_action ( 'admin_enqueue_scripts ' , [ $ this , 'enqueue_admin_assets ' ] );
110+ }
111+
112+ /**
113+ * Enqueue the Classic Editor polling script. Loaded in Classic Editor only when the feature is enabled.
114+ *
115+ * @param string $hook_suffix The current admin page.
116+ */
117+ public function enqueue_admin_assets ( string $ hook_suffix ) {
118+ if ( 'post.php ' !== $ hook_suffix && 'post-new.php ' !== $ hook_suffix ) {
119+ return ;
120+ }
121+
122+ if ( ! $ this ->is_feature_enabled () ) {
123+ return ;
124+ }
125+
126+ $ screen = get_current_screen ();
127+
128+ if ( ! $ screen || $ screen ->is_block_editor () ) {
129+ return ;
130+ }
131+
132+ if ( ! in_array ( $ screen ->post_type , $ this ->get_supported_post_types (), true ) ) {
133+ return ;
134+ }
135+
136+ wp_enqueue_script (
137+ 'classifai-plugin-classic-text-to-speech ' ,
138+ CLASSIFAI_PLUGIN_URL . 'dist/classifai-plugin-classic-text-to-speech.js ' ,
139+ get_asset_info ( 'classifai-plugin-classic-text-to-speech ' , 'dependencies ' ),
140+ get_asset_info ( 'classifai-plugin-classic-text-to-speech ' , 'version ' ),
141+ true
142+ );
108143 }
109144
110145 /**
@@ -419,6 +454,36 @@ public function add_meta_box( string $post_type ) {
419454 public function render_meta_box ( \WP_Post $ post ) {
420455 wp_nonce_field ( 'classifai_text_to_speech_meta_action ' , 'classifai_text_to_speech_meta ' );
421456
457+ $ is_as_scheduled_job =
458+ function_exists ( 'as_has_scheduled_action ' ) &&
459+ \as_has_scheduled_action (
460+ 'classifai_schedule_text_to_speech_job ' ,
461+ [
462+ 'post_id ' => (int ) $ post ->ID ,
463+ 'calling_user_id ' => get_current_user_id (),
464+ ],
465+ 'classifai '
466+ );
467+
468+ if ( $ is_as_scheduled_job ) :
469+ ?>
470+ <p class="classifai-tts-status" data-post-id="<?php echo esc_attr ( (string ) $ post ->ID ); ?> " data-nonce="<?php echo esc_attr ( wp_create_nonce ( 'classifai_tts_status ' ) ); ?> ">
471+ <?php esc_html_e ( 'Audio generation is in progress… ' , 'classifai ' ); ?>
472+ </p>
473+ <?php
474+ else :
475+ $ this ->render_audio_generation_ui ( $ post );
476+ endif ;
477+ }
478+
479+ /**
480+ * Render the post-generation UI for the TTS meta box. Used both on initial
481+ * render and via AJAX after a queued job completes so the meta box
482+ * matches a fresh page load.
483+ *
484+ * @param \WP_Post $post WP_Post object.
485+ */
486+ public function render_audio_generation_ui ( \WP_Post $ post ) {
422487 $ source_url = false ;
423488 $ audio_id = get_post_meta ( $ post ->ID , self ::AUDIO_ID_KEY , true );
424489
@@ -445,24 +510,7 @@ public function render_meta_box( \WP_Post $post ) {
445510 if ( $ post_type ) {
446511 $ post_type_label = $ post_type ->labels ->singular_name ;
447512 }
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 : ?>
465-
513+ ?>
466514 <p>
467515 <label for="classifai_synthesize_speech">
468516 <input type="checkbox" value="1" id="classifai_synthesize_speech" name="classifai_synthesize_speech" <?php checked ( $ process_content ); ?> />
@@ -488,8 +536,6 @@ function_exists( 'as_has_scheduled_action' ) &&
488536 </span>
489537 </p>
490538
491- <?php endif ; ?>
492-
493539 <?php
494540 if ( $ source_url ) {
495541 $ cache_busting_url = add_query_arg (
@@ -499,15 +545,66 @@ function_exists( 'as_has_scheduled_action' ) &&
499545 $ source_url
500546 );
501547 ?>
502-
503548 <p>
504549 <audio id="classifai-audio-preview" controls controlslist="nodownload" src="<?php echo esc_url ( $ cache_busting_url ); ?> "></audio>
505550 </p>
506-
507551 <?php
508552 }
509553 }
510554
555+ /**
556+ * AJAX handler for the Classic Editor meta box's polling. Returns the
557+ * current audio generation status for the given post id.
558+ */
559+ public function ajax_get_audio_generation_status () {
560+ check_ajax_referer ( 'classifai_tts_status ' , 'nonce ' );
561+
562+ $ post_id = isset ( $ _POST ['post_id ' ] ) ? absint ( wp_unslash ( $ _POST ['post_id ' ] ) ) : 0 ;
563+
564+ if ( ! $ post_id || ! current_user_can ( 'edit_post ' , $ post_id ) ) {
565+ wp_send_json_error ( [ 'message ' => __ ( 'Invalid post. ' , 'classifai ' ) ], 403 );
566+ }
567+
568+ $ in_progress =
569+ function_exists ( 'as_has_scheduled_action ' ) &&
570+ \as_has_scheduled_action (
571+ 'classifai_schedule_text_to_speech_job ' ,
572+ [
573+ 'post_id ' => $ post_id ,
574+ 'calling_user_id ' => get_current_user_id (),
575+ ],
576+ 'classifai '
577+ );
578+
579+ $ error_message = '' ;
580+ $ raw_error = get_post_meta ( $ post_id , '_classifai_text_to_speech_error ' , true );
581+
582+ if ( ! empty ( $ raw_error ) ) {
583+ $ decoded = (array ) json_decode ( (string ) $ raw_error );
584+ if ( ! empty ( $ decoded ['message ' ] ) ) {
585+ $ error_message = (string ) $ decoded ['message ' ];
586+ }
587+ }
588+
589+ $ html = '' ;
590+ if ( ! $ in_progress && ! $ error_message ) {
591+ $ post = get_post ( $ post_id );
592+ if ( $ post ) {
593+ ob_start ();
594+ $ this ->render_audio_generation_ui ( $ post );
595+ $ html = (string ) ob_get_clean ();
596+ }
597+ }
598+
599+ wp_send_json_success (
600+ [
601+ 'inProgress ' => (bool ) $ in_progress ,
602+ 'error ' => $ error_message ,
603+ 'html ' => $ html ,
604+ ]
605+ );
606+ }
607+
511608 /**
512609 * Process the meta box save.
513610 *
0 commit comments