diff --git a/includes/Classifai/Features/TextToSpeech.php b/includes/Classifai/Features/TextToSpeech.php index 6e5ca065b..ad525b3b1 100644 --- a/includes/Classifai/Features/TextToSpeech.php +++ b/includes/Classifai/Features/TextToSpeech.php @@ -398,7 +398,8 @@ public function rest_endpoint_callback( WP_REST_Request $request ) { $results = $this->run( $request->get_param( 'id' ), 'synthesize' ); if ( $results && ! is_wp_error( $results ) ) { - $attachment_id = $this->save( $results, $request->get_param( 'id' ) ); + // An integer result means the provider returned a cached attachment ID + $attachment_id = is_int( $results ) ? $results : $this->save( $results, $request->get_param( 'id' ) ); if ( ! is_wp_error( $attachment_id ) ) { return rest_ensure_response( @@ -676,7 +677,10 @@ public function generate_text_to_speech_audio( int $post_id, ?int $calling_user_ $results = $this->run( $post_id, 'synthesize' ); if ( $results && ! is_wp_error( $results ) ) { - $this->save( $results, $post_id ); + // An integer result means the provider returned a cached attachment ID + if ( ! is_int( $results ) ) { + $this->save( $results, $post_id ); + } delete_post_meta( $post_id, '_classifai_text_to_speech_error' ); } elseif ( is_wp_error( $results ) ) { update_post_meta( diff --git a/tests/Integration/Features/TextToSpeechTest.php b/tests/Integration/Features/TextToSpeechTest.php index 8f54ecdec..a3ec19b79 100644 --- a/tests/Integration/Features/TextToSpeechTest.php +++ b/tests/Integration/Features/TextToSpeechTest.php @@ -125,4 +125,32 @@ public function test_save_replaces_existing_audio() { $this->assertSame( $second, (int) get_post_meta( $post_id, TextToSpeech::AUDIO_ID_KEY, true ) ); $this->assertNull( get_post( $first ), 'The old audio attachment is deleted.' ); } + + /** + * When the provider returns a cached attachment ID (content unchanged), the + * existing audio must be left intact rather than overwritten with the + * numeric ID coerced to a string. + * + * @covers ::generate_text_to_speech_audio + */ + public function test_cached_audio_is_not_overwritten() { + $feature = new TextToSpeech(); + $post_id = self::factory()->post->create( [ 'post_content' => 'Some content to read.' ] ); + $this->as_user_with_role( 'administrator' ); + $this->enable_feature(); + + // Seed a previously generated audio attachment. + $attachment_id = $feature->save( 'real-audio-bytes', $post_id ); + $audio_file = get_attached_file( $attachment_id ); + + // Mark the saved content hash so the provider's cached branch is taken. + update_post_meta( $post_id, TextToSpeech::AUDIO_HASH_KEY, md5( $feature->normalize_post_content( $post_id ) ) ); + + $feature->generate_text_to_speech_audio( $post_id ); + + // The attachment must be untouched: same ID, same file, same bytes. + $this->assertSame( $attachment_id, (int) get_post_meta( $post_id, TextToSpeech::AUDIO_ID_KEY, true ) ); + $this->assertNotNull( get_post( $attachment_id ), 'The cached audio attachment is preserved.' ); + $this->assertSame( 'real-audio-bytes', file_get_contents( $audio_file ) ); + } }