302 lines
8.8 KiB
PHP
302 lines
8.8 KiB
PHP
<?php
|
|
/**
|
|
* Eleven Labs API integration class
|
|
*/
|
|
class TWP_ElevenLabs_API {
|
|
|
|
private $api_key;
|
|
private $voice_id;
|
|
private $model_id;
|
|
private $api_base = 'https://api.elevenlabs.io/v1';
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
$this->api_key = get_option('twp_elevenlabs_api_key');
|
|
$this->voice_id = get_option('twp_elevenlabs_voice_id');
|
|
$this->model_id = get_option('twp_elevenlabs_model_id', 'eleven_multilingual_v2');
|
|
}
|
|
|
|
/**
|
|
* Convert text to speech
|
|
*/
|
|
public function text_to_speech($text, $voice_id = null) {
|
|
// Handle both string voice_id and options array
|
|
if (is_array($voice_id)) {
|
|
$options = $voice_id;
|
|
$voice_id = isset($options['voice_id']) ? $options['voice_id'] : null;
|
|
}
|
|
|
|
if (!$voice_id) {
|
|
$voice_id = $this->voice_id;
|
|
}
|
|
|
|
$url = $this->api_base . '/text-to-speech/' . $voice_id;
|
|
|
|
$data = array(
|
|
'text' => $text,
|
|
'model_id' => $this->model_id,
|
|
'voice_settings' => array(
|
|
'stability' => 0.5,
|
|
'similarity_boost' => 0.5
|
|
)
|
|
);
|
|
|
|
$response = $this->make_request('POST', $url, $data);
|
|
|
|
if ($response['success']) {
|
|
// Save audio file
|
|
$upload_dir = wp_upload_dir();
|
|
$filename = 'tts_' . uniqid() . '.mp3';
|
|
$file_path = $upload_dir['path'] . '/' . $filename;
|
|
$file_url = $upload_dir['url'] . '/' . $filename;
|
|
|
|
file_put_contents($file_path, $response['data']);
|
|
|
|
return array(
|
|
'success' => true,
|
|
'file_path' => $file_path,
|
|
'file_url' => $file_url
|
|
);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Get available voices
|
|
*/
|
|
public function get_voices() {
|
|
$url = $this->api_base . '/voices';
|
|
$response = $this->make_request('GET', $url);
|
|
|
|
if ($response['success'] && isset($response['data']['voices'])) {
|
|
// Cache voices for 1 hour to reduce API calls
|
|
set_transient('twp_elevenlabs_voices', $response['data']['voices'], HOUR_IN_SECONDS);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Get cached voices or fetch from API
|
|
*/
|
|
public function get_cached_voices() {
|
|
$cached_voices = get_transient('twp_elevenlabs_voices');
|
|
|
|
if ($cached_voices !== false) {
|
|
return array(
|
|
'success' => true,
|
|
'data' => array('voices' => $cached_voices)
|
|
);
|
|
}
|
|
|
|
return $this->get_voices();
|
|
}
|
|
|
|
/**
|
|
* Get voice details
|
|
*/
|
|
public function get_voice($voice_id) {
|
|
$url = $this->api_base . '/voices/' . $voice_id;
|
|
return $this->make_request('GET', $url);
|
|
}
|
|
|
|
/**
|
|
* Get user subscription info
|
|
*/
|
|
public function get_subscription_info() {
|
|
$url = $this->api_base . '/user/subscription';
|
|
return $this->make_request('GET', $url);
|
|
}
|
|
|
|
/**
|
|
* Get available models
|
|
*/
|
|
public function get_models() {
|
|
$url = $this->api_base . '/models';
|
|
$response = $this->make_request('GET', $url);
|
|
|
|
if ($response['success'] && isset($response['data'])) {
|
|
// Filter models that support text-to-speech
|
|
$tts_models = array();
|
|
foreach ($response['data'] as $model) {
|
|
if (isset($model['can_do_text_to_speech']) && $model['can_do_text_to_speech']) {
|
|
$tts_models[] = $model;
|
|
}
|
|
}
|
|
|
|
// Cache models for 1 hour
|
|
set_transient('twp_elevenlabs_models', $tts_models, HOUR_IN_SECONDS);
|
|
|
|
return array(
|
|
'success' => true,
|
|
'data' => $tts_models
|
|
);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Get cached models or fetch from API
|
|
*/
|
|
public function get_cached_models() {
|
|
$cached_models = get_transient('twp_elevenlabs_models');
|
|
|
|
if ($cached_models !== false) {
|
|
return array(
|
|
'success' => true,
|
|
'data' => $cached_models
|
|
);
|
|
}
|
|
|
|
return $this->get_models();
|
|
}
|
|
|
|
/**
|
|
* Generate speech for IVR menu
|
|
*/
|
|
public function generate_ivr_prompt($text, $options = array()) {
|
|
$default_options = array(
|
|
'voice_id' => $this->voice_id,
|
|
'stability' => 0.75,
|
|
'similarity_boost' => 0.75,
|
|
'style' => 0,
|
|
'use_speaker_boost' => true
|
|
);
|
|
|
|
$options = wp_parse_args($options, $default_options);
|
|
|
|
$url = $this->api_base . '/text-to-speech/' . $options['voice_id'];
|
|
|
|
$data = array(
|
|
'text' => $text,
|
|
'model_id' => $this->model_id,
|
|
'voice_settings' => array(
|
|
'stability' => $options['stability'],
|
|
'similarity_boost' => $options['similarity_boost'],
|
|
'style' => $options['style'],
|
|
'use_speaker_boost' => $options['use_speaker_boost']
|
|
)
|
|
);
|
|
|
|
return $this->make_request('POST', $url, $data);
|
|
}
|
|
|
|
/**
|
|
* Generate speech for queue messages
|
|
*/
|
|
public function generate_queue_messages($messages) {
|
|
$generated_files = array();
|
|
|
|
foreach ($messages as $key => $message) {
|
|
$result = $this->text_to_speech($message);
|
|
|
|
if ($result['success']) {
|
|
$generated_files[$key] = $result['file_url'];
|
|
}
|
|
}
|
|
|
|
return $generated_files;
|
|
}
|
|
|
|
/**
|
|
* Stream text to speech (for real-time applications)
|
|
*/
|
|
public function stream_text_to_speech($text, $voice_id = null) {
|
|
if (!$voice_id) {
|
|
$voice_id = $this->voice_id;
|
|
}
|
|
|
|
$url = $this->api_base . '/text-to-speech/' . $voice_id . '/stream';
|
|
|
|
$data = array(
|
|
'text' => $text,
|
|
'model_id' => $this->model_id,
|
|
'voice_settings' => array(
|
|
'stability' => 0.5,
|
|
'similarity_boost' => 0.5
|
|
),
|
|
'optimize_streaming_latency' => 3
|
|
);
|
|
|
|
return $this->make_request('POST', $url, $data, true);
|
|
}
|
|
|
|
/**
|
|
* Make API request
|
|
*/
|
|
private function make_request($method, $url, $data = array(), $stream = false) {
|
|
$args = array(
|
|
'method' => $method,
|
|
'headers' => array(
|
|
'xi-api-key' => $this->api_key,
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => $stream ? 'audio/mpeg' : 'application/json'
|
|
),
|
|
'timeout' => 60
|
|
);
|
|
|
|
if ($method === 'POST' && !empty($data)) {
|
|
$args['body'] = json_encode($data);
|
|
}
|
|
|
|
if ($method === 'GET') {
|
|
$response = wp_remote_get($url, $args);
|
|
} else {
|
|
$response = wp_remote_post($url, $args);
|
|
}
|
|
|
|
if (is_wp_error($response)) {
|
|
return array(
|
|
'success' => false,
|
|
'error' => $response->get_error_message()
|
|
);
|
|
}
|
|
|
|
$status_code = wp_remote_retrieve_response_code($response);
|
|
$body = wp_remote_retrieve_body($response);
|
|
|
|
if ($status_code >= 200 && $status_code < 300) {
|
|
if ($stream || strpos(wp_remote_retrieve_header($response, 'content-type'), 'audio') !== false) {
|
|
// Return raw audio data
|
|
return array(
|
|
'success' => true,
|
|
'data' => $body
|
|
);
|
|
} else {
|
|
$decoded = json_decode($body, true);
|
|
return array(
|
|
'success' => true,
|
|
'data' => $decoded
|
|
);
|
|
}
|
|
} else {
|
|
$decoded = json_decode($body, true);
|
|
return array(
|
|
'success' => false,
|
|
'error' => isset($decoded['detail']) ? $decoded['detail'] : 'API request failed',
|
|
'code' => $status_code
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cache generated audio
|
|
*/
|
|
public function cache_audio($text, $audio_data) {
|
|
$cache_key = 'twp_tts_' . md5($text . $this->voice_id);
|
|
set_transient($cache_key, $audio_data, DAY_IN_SECONDS * 7);
|
|
return $cache_key;
|
|
}
|
|
|
|
/**
|
|
* Get cached audio
|
|
*/
|
|
public function get_cached_audio($text) {
|
|
$cache_key = 'twp_tts_' . md5($text . $this->voice_id);
|
|
return get_transient($cache_key);
|
|
}
|
|
} |