CodeWP Eleven Labs TTS Plugin
The CodeWP Eleven Labs TTS Plugin for WordPress allows site owners to integrate Eleven Labs' advanced Text-to-Speech technology into their website. Users can select from different voices and generate audio versions of their content, making their posts more accessible and engaging.
<?php /* Plugin Name: CodeWP Eleven Labs Text-to-Speech Integration Description: Integrate Eleven Labs Text-to-Speech into your WordPress site. Version: 1.4 Author: Your Name License: GPL2 */ if ( ! defined( 'ABSPATH' ) ) { exit; } class CWP_ElevenLabs_TTS_Plugin { private $option_name = 'cwp_elevenlabs_tts_options'; public function __construct() { // Add settings menu add_action( 'admin_menu', array( $this, 'cwp_add_settings_page' ) ); // Register settings add_action( 'admin_init', array( $this, 'cwp_register_settings' ) ); // Add meta box to post editor add_action( 'add_meta_boxes', array( $this, 'cwp_add_voice_meta_box' ) ); // Enqueue scripts add_action( 'admin_enqueue_scripts', array( $this, 'cwp_enqueue_admin_scripts' ) ); // Save post meta add_action( 'save_post', array( $this, 'cwp_save_post_meta' ) ); // AJAX handler add_action( 'wp_ajax_cwp_generate_audio', array( $this, 'cwp_ajax_generate_audio' ) ); // Inject audio into content add_filter( 'the_content', array( $this, 'cwp_append_audio_to_content' ) ); // Add custom column to admin post list add_filter( 'manage_post_posts_columns', array( $this, 'cwp_add_audio_column' ) ); add_action( 'manage_post_posts_custom_column', array( $this, 'cwp_render_audio_column' ), 10, 2 ); } // Add settings page public function cwp_add_settings_page() { add_options_page( 'CodeWP Eleven Labs TTS Settings', 'CodeWP Eleven Labs TTS', 'manage_options', 'cwp-elevenlabs-tts-settings', array( $this, 'cwp_render_settings_page' ) ); } // Register settings public function cwp_register_settings() { register_setting( 'cwp_elevenlabs_tts_settings', $this->option_name, array( $this, 'cwp_sanitize_settings' ) ); add_settings_section( 'cwp_elevenlabs_tts_main_section', 'Main Settings', null, 'cwp-elevenlabs-tts-settings' ); add_settings_field( 'api_key', 'API Key', array( $this, 'cwp_render_api_key_field' ), 'cwp-elevenlabs-tts-settings', 'cwp_elevenlabs_tts_main_section' ); add_settings_field( 'default_voice', 'Default Voice', array( $this, 'cwp_render_default_voice_field' ), 'cwp-elevenlabs-tts-settings', 'cwp_elevenlabs_tts_main_section' ); add_settings_field( 'display_model_name', 'Display Model Name', array( $this, 'cwp_render_display_model_name_field' ), 'cwp-elevenlabs-tts-settings', 'cwp_elevenlabs_tts_main_section' ); add_settings_field( 'elevenlabs_link', 'Eleven Labs Platform', array( $this, 'cwp_render_elevenlabs_link_field' ), 'cwp-elevenlabs-tts-settings', 'cwp_elevenlabs_tts_main_section' ); } // Sanitize settings public function cwp_sanitize_settings( $input ) { $output = array(); if ( isset( $input['api_key'] ) ) { $output['api_key'] = sanitize_text_field( $input['api_key'] ); } if ( isset( $input['default_voice'] ) ) { $output['default_voice'] = sanitize_text_field( $input['default_voice'] ); } if ( isset( $input['display_model_name'] ) ) { $output['display_model_name'] = sanitize_text_field( $input['display_model_name'] ); } else { $output['display_model_name'] = '0'; } return $output; } // Render settings page public function cwp_render_settings_page() { ?> <div class="wrap"> <h1>CodeWP Eleven Labs Text-to-Speech Settings</h1> <form method="post" action="options.php"> <?php settings_fields( 'cwp_elevenlabs_tts_settings' ); do_settings_sections( 'cwp-elevenlabs-tts-settings' ); submit_button(); ?> </form> </div> <?php } // Render API Key field public function cwp_render_api_key_field() { $options = get_option( $this->option_name ); ?> <input type="text" name="<?php echo esc_attr( $this->option_name ); ?>[api_key]" value="<?php echo isset( $options['api_key'] ) ? esc_attr( $options['api_key'] ) : ''; ?>" size="50" /> <?php } // Render Default Voice field public function cwp_render_default_voice_field() { $options = get_option( $this->option_name ); $api_key = isset( $options['api_key'] ) ? $options['api_key'] : ''; if ( ! empty( $api_key ) ) { $voices = $this->cwp_get_voices( $api_key ); if ( ! is_wp_error( $voices ) ) { $default_voice = isset( $options['default_voice'] ) ? $options['default_voice'] : ''; ?> <select name="<?php echo esc_attr( $this->option_name ); ?>[default_voice]"> <?php foreach ( $voices as $voice ) : ?> <option value="<?php echo esc_attr( $voice['voice_id'] ); ?>" <?php selected( $default_voice, $voice['voice_id'] ); ?>> <?php echo esc_html( $voice['name'] ); ?> </option> <?php endforeach; ?> </select> <?php } else { echo '<p style="color:red;">Error fetching voices. Please check your API key.</p>'; } } else { echo '<p>Please enter your API key and save to select a default voice.</p>'; } } // Render Display Model Name field public function cwp_render_display_model_name_field() { $options = get_option( $this->option_name ); $checked = isset( $options['display_model_name'] ) && $options['display_model_name'] === '1' ? 'checked' : ''; ?> <label> <input type="checkbox" name="<?php echo esc_attr( $this->option_name ); ?>[display_model_name]" value="1" <?php echo esc_attr( $checked ); ?> /> Display the model name with the audio player on posts. </label> <?php } // Render Eleven Labs link public function cwp_render_elevenlabs_link_field() { echo '<a href="https://elevenlabs.io/" target="_blank">Visit Eleven Labs Platform</a>'; } // Get voices from API private function cwp_get_voices( $api_key ) { $response = wp_remote_get( 'https://api.elevenlabs.io/v1/voices', array( 'headers' => array( 'xi-api-key' => $api_key, ), 'timeout' => 60, ) ); if ( is_wp_error( $response ) ) { return $response; } $code = wp_remote_retrieve_response_code( $response ); if ( $code != 200 ) { $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); $error_message = isset( $data['detail'] ) ? $data['detail'] : 'Error fetching voices from API.'; return new WP_Error( 'api_error', $error_message ); } $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body, true ); if ( ! isset( $data['voices'] ) ) { return new WP_Error( 'api_error', 'Invalid response from API' ); } return $data['voices']; } // Add meta box to post editor public function cwp_add_voice_meta_box() { add_meta_box( 'cwp_elevenlabs_tts_meta_box', 'CodeWP Eleven Labs TTS', array( $this, 'cwp_render_voice_meta_box' ), 'post', 'side', 'default' ); } // Render meta box public function cwp_render_voice_meta_box( $post ) { wp_nonce_field( 'cwp_elevenlabs_tts_meta_box', 'cwp_elevenlabs_tts_meta_box_nonce' ); $options = get_option( $this->option_name ); $api_key = isset( $options['api_key'] ) ? $options['api_key'] : ''; if ( empty( $api_key ) ) { echo '<p>Please set your API key in the plugin settings.</p>'; return; } $voices = $this->cwp_get_voices( $api_key ); if ( is_wp_error( $voices ) ) { echo '<p style="color:red;">' . esc_html( $voices->get_error_message() ) . '</p>'; return; } $selected_voice = get_post_meta( $post->ID, '_cwp_elevenlabs_tts_voice_id', true ); if ( empty( $selected_voice ) ) { $selected_voice = isset( $options['default_voice'] ) ? $options['default_voice'] : ''; } $audio_url = get_post_meta( $post->ID, '_cwp_elevenlabs_tts_audio_url', true ); $model_name = get_post_meta( $post->ID, '_cwp_elevenlabs_tts_model_name', true ); ?> <p> <label for="cwp_elevenlabs_tts_voice_id">Select Voice:</label> <select name="cwp_elevenlabs_tts_voice_id" id="cwp_elevenlabs_tts_voice_id"> <?php foreach ( $voices as $voice ) : ?> <option value="<?php echo esc_attr( $voice['voice_id'] ); ?>" <?php selected( $selected_voice, $voice['voice_id'] ); ?>> <?php echo esc_html( $voice['name'] ); ?> </option> <?php endforeach; ?> </select> </p> <?php if ( $audio_url ) : ?> <p> <strong>Audio generated.</strong> </p> <p> <audio controls> <source src="<?php echo esc_url( $audio_url ); ?>" type="audio/mpeg"> Your browser does not support the audio element. </audio> </p> <?php if ( $model_name ) : ?> <p><strong>Model Used:</strong> <?php echo esc_html( $model_name ); ?></p> <?php endif; ?> <p> <button type="button" class="button cwp-generate-audio-button" data-action="regenerate">Regenerate Audio</button> </p> <?php else : ?> <p> <button type="button" class="button cwp-generate-audio-button" data-action="generate">Generate Audio</button> </p> <?php endif; ?> <div class="cwp-audio-generation-status"></div> <?php } // Save post meta public function cwp_save_post_meta( $post_id ) { // Check if our nonce is set. if ( ! isset( $_POST['cwp_elevenlabs_tts_meta_box_nonce'] ) ) { return; } // Verify that the nonce is valid. if ( ! wp_verify_nonce( $_POST['cwp_elevenlabs_tts_meta_box_nonce'], 'cwp_elevenlabs_tts_meta_box' ) ) { return; } // If this is an autosave, our form has not been submitted, so we don't want to do anything. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Check the user's permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } if ( isset( $_POST['cwp_elevenlabs_tts_voice_id'] ) ) { $voice_id = sanitize_text_field( $_POST['cwp_elevenlabs_tts_voice_id'] ); update_post_meta( $post_id, '_cwp_elevenlabs_tts_voice_id', $voice_id ); } } // Enqueue admin scripts public function cwp_enqueue_admin_scripts( $hook ) { global $post; if ( 'post.php' != $hook && 'post-new.php' != $hook ) { return; } if ( 'post' != $post->post_type ) { return; } wp_enqueue_script( 'jquery' ); wp_add_inline_script( 'jquery', ' jQuery(document).ready(function($){ $(".cwp-generate-audio-button").on("click", function(e){ e.preventDefault(); var button = $(this); var action = button.data("action"); var post_id = ' . $post->ID . '; var voice_id = $("#cwp_elevenlabs_tts_voice_id").val(); var status_div = $(".cwp-audio-generation-status"); status_div.html("<p>Generating audio, please wait...</p>"); $.ajax({ url: ajaxurl, type: "POST", data: { action: "cwp_generate_audio", nonce: "' . wp_create_nonce( 'cwp_generate_audio_nonce' ) . '", post_id: post_id, voice_id: voice_id, }, success: function(response) { if (response.success) { status_div.html("<p>Audio generated successfully. Please save/update the post to refresh the meta box.</p>"); } else { status_div.html("<p style=\'color:red;\'>" + response.data + "</p>"); } }, error: function() { status_div.html("<p style=\'color:red;\'>An error occurred while generating audio.</p>"); } }); }); }); ' ); } // AJAX handler for generating audio public function cwp_ajax_generate_audio() { // Check nonce check_ajax_referer( 'cwp_generate_audio_nonce', 'nonce' ); // Check permissions if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( 'You are not allowed to perform this action.' ); } $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; $voice_id = isset( $_POST['voice_id'] ) ? sanitize_text_field( $_POST['voice_id'] ) : ''; if ( ! $post_id || ! $voice_id ) { wp_send_json_error( 'Invalid post ID or voice ID.' ); } $post = get_post( $post_id ); if ( ! $post ) { wp_send_json_error( 'Post not found.' ); } // Save the selected voice update_post_meta( $post_id, '_cwp_elevenlabs_tts_voice_id', $voice_id ); $options = get_option( $this->option_name ); $api_key = isset( $options['api_key'] ) ? $options['api_key'] : ''; if ( empty( $api_key ) ) { wp_send_json_error( 'API key is not set.' ); } $text = strip_tags( $post->post_content ); // Limit text length to 10,000 characters $text = mb_substr( $text, 0, 10000 ); $result = $this->cwp_generate_audio( $api_key, $voice_id, $text, $post_id ); if ( $result && ! is_wp_error( $result ) ) { update_post_meta( $post_id, '_cwp_elevenlabs_tts_audio_url', $result['audio_url'] ); update_post_meta( $post_id, '_cwp_elevenlabs_tts_model_name', $result['model_name'] ); wp_send_json_success( 'Audio generated successfully.' ); } else { $error_message = is_wp_error( $result ) ? $result->get_error_message() : 'Failed to generate audio.'; wp_send_json_error( $error_message ); } } // Generate audio using API private function cwp_generate_audio( $api_key, $voice_id, $text, $post_id ) { $url = 'https://api.elevenlabs.io/v1/text-to-speech/' . $voice_id; $args = array( 'headers' => array( 'xi-api-key' => $api_key, 'Accept' => 'audio/mpeg', 'Content-Type' => 'application/json', ), 'body' => wp_json_encode( array( 'text' => $text, ) ), 'timeout' => 60, ); $response = wp_remote_post( $url, $args ); if ( is_wp_error( $response ) ) { return $response; } $code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( $code != 200 ) { // Handle different error response formats $data = json_decode( $body, true ); $error_message = 'Failed to generate audio.'; if ( isset( $data['detail'] ) ) { $error_message = $data['detail']; } elseif ( isset( $data['message'] ) ) { $error_message = $data['message']; } elseif ( isset( $data['data']['message'] ) ) { $error_message = $data['data']['message']; } return new WP_Error( 'api_error', $error_message ); } $audio_data = $body; // Save audio file to media library $upload_dir = wp_upload_dir(); $filename = 'cwp-elevenlabs-' . $post_id . '-' . time() . '.mp3'; $audio_file = $upload_dir['path'] . '/' . $filename; $file_saved = file_put_contents( $audio_file, $audio_data ); if ( $file_saved === false ) { return new WP_Error( 'file_save_error', 'Failed to save audio file.' ); } // Check file type and set proper MIME type $filetype = wp_check_filetype( $filename, null ); // Prepare an array of post data for the attachment. $attachment = array( 'guid' => $upload_dir['url'] . '/' . $filename, 'post_mime_type' => $filetype['type'], 'post_title' => 'CodeWP ElevenLabs Audio for Post ' . $post_id, 'post_content' => '', 'post_status' => 'inherit' ); // Insert the attachment. $attach_id = wp_insert_attachment( $attachment, $audio_file, $post_id ); // Generate the metadata for the attachment, and update the database record. require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attach_data = wp_generate_attachment_metadata( $attach_id, $audio_file ); wp_update_attachment_metadata( $attach_id, $attach_data ); // Get the model name (voice name) $voice_name = $this->cwp_get_voice_name( $api_key, $voice_id ); return array( 'audio_url' => wp_get_attachment_url( $attach_id ), 'model_name' => $voice_name, ); } // Get voice name by voice_id private function cwp_get_voice_name( $api_key, $voice_id ) { $voices = $this->cwp_get_voices( $api_key ); if ( is_wp_error( $voices ) ) { return ''; } foreach ( $voices as $voice ) { if ( $voice['voice_id'] === $voice_id ) { return $voice['name']; } } return ''; } // Append audio to content public function cwp_append_audio_to_content( $content ) { if ( is_singular( 'post' ) ) { $post_id = get_the_ID(); $audio_url = get_post_meta( $post_id, '_cwp_elevenlabs_tts_audio_url', true ); if ( $audio_url ) { $audio_player = do_shortcode( '[audio src="' . esc_url( $audio_url ) . '"]' ); $options = get_option( $this->option_name ); $display_model_name = isset( $options['display_model_name'] ) && $options['display_model_name'] === '1'; $model_name = get_post_meta( $post_id, '_cwp_elevenlabs_tts_model_name', true ); $model_name_html = ''; if ( $display_model_name && $model_name ) { $model_name_html = '<p><em>Audio generated using model: ' . esc_html( $model_name ) . '</em></p>'; } $content = $audio_player . $model_name_html . $content; } } return $content; } // Add custom column to admin post list public function cwp_add_audio_column( $columns ) { $columns['cwp_audio'] = 'Audio'; return $columns; } // Render custom column content public function cwp_render_audio_column( $column, $post_id ) { if ( 'cwp_audio' === $column ) { $audio_url = get_post_meta( $post_id, '_cwp_elevenlabs_tts_audio_url', true ); if ( $audio_url ) { echo '<span style="color:green;">✓</span>'; } else { echo ''; } } } } new CWP_ElevenLabs_TTS_Plugin();
Frequently Asked Questions
This plugin integrates Eleven Labs' Text-to-Speech API with your WordPress site to generate audio versions of your posts.