use reqwest::Client;
pub mod error;
pub mod models;
pub mod types;
pub mod voices;
pub use error::ElevenLabsTTDError;
pub use types::*;
#[derive(Clone)]
pub struct ElevenLabsTTDClient {
client: Client,
api_key: String,
base_url: String,
}
impl ElevenLabsTTDClient {
pub fn new<S: Into<String>>(api_key: S) -> Self {
Self {
client: Client::new(),
api_key: api_key.into(),
base_url: "https://api.elevenlabs.io/v1".to_string(),
}
}
pub fn with_base_url<S: Into<String>>(api_key: S, base_url: S) -> Self {
Self {
client: Client::new(),
api_key: api_key.into(),
base_url: base_url.into(),
}
}
pub fn text_to_dialogue<I: Into<Vec<TTDInput>>>(&self, inputs: I) -> TextToDialogueBuilder {
TextToDialogueBuilder::new(self.clone(), inputs.into())
}
pub(crate) async fn execute_ttd(
&self,
request: TTDRequest,
) -> Result<Vec<u8>, ElevenLabsTTDError> {
let mut url = format!("{}/text-to-dialogue", self.base_url);
if request.output_format.is_some() {
url = format!(
"{}?output_format={}",
url,
request.output_format.clone().unwrap()
);
}
let response = self
.client
.post(&url)
.header("xi-api-key", &self.api_key)
.header("Content-Type", "application/json")
.json(&request)
.send()
.await?;
if !response.status().is_success() {
return Err(ElevenLabsTTDError::ApiError {
status: response.status().as_u16(),
message: response.text().await.unwrap_or_default(),
});
}
Ok(response.bytes().await?.to_vec())
}
}
pub struct TextToDialogueBuilder {
client: ElevenLabsTTDClient,
inputs: Vec<TTDInput>,
output_format: Option<String>,
model_id: Option<String>,
settings: Option<TTDSettings>,
pronunciation_dictionary_locators: Option<TTDPronunciationDictionaryLocators>,
seed: Option<u32>,
}
impl TextToDialogueBuilder {
fn new(client: ElevenLabsTTDClient, inputs: Vec<TTDInput>) -> Self {
Self {
client,
inputs,
output_format: None,
model_id: None,
settings: None,
pronunciation_dictionary_locators: None,
seed: None,
}
}
pub fn output_format<S: Into<String>>(mut self, output_format: S) -> Self {
self.output_format = Some(output_format.into());
self
}
pub fn model<S: Into<String>>(mut self, model_id: S) -> Self {
self.model_id = Some(model_id.into());
self
}
pub fn settings(mut self, settings: TTDSettings) -> Self {
self.settings = Some(settings);
self
}
pub fn pronunciation_dictionary_locators(
mut self,
pronunciation_dictionary_locators: TTDPronunciationDictionaryLocators,
) -> Self {
self.pronunciation_dictionary_locators = Some(pronunciation_dictionary_locators);
self
}
pub fn seed(mut self, seed: u32) -> Self {
self.seed = Some(seed);
self
}
pub async fn execute(self) -> Result<Vec<u8>, ElevenLabsTTDError> {
let output_format = self
.output_format
.unwrap_or_else(|| "mp3_44100_128".to_string());
let request = TTDRequest {
inputs: self.inputs,
output_format: Some(output_format.clone()),
seed: self.seed.or(None),
model_id: self
.model_id
.unwrap_or_else(|| models::elevanlabs_models::ELEVEN_V3.to_string()), settings: self.settings.or(None),
pronunciation_dictionary_locators: self.pronunciation_dictionary_locators.or(None),
};
self.client.execute_ttd(request).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_client_creation() {
let client = ElevenLabsTTDClient::new("test-key");
assert_eq!(client.api_key, "test-key");
}
#[test]
fn test_builder_pattern() {
let client = ElevenLabsTTDClient::new("test-key");
let builder = client.text_to_dialogue([]).model("model-456");
assert!(builder.inputs.is_empty());
assert_eq!(builder.model_id, Some("model-456".to_string()));
}
}