tts_rust/
tts.rs

1use minreq::get;
2use std::fs;
3use std::fs::File;
4use std::io::prelude::*;
5use std::io::BufReader;
6use std::path::Path;
7
8use crate::constants::GOOGLE_TTS_MAX_CHARS;
9use crate::languages::Languages;
10use crate::url::core::Core;
11
12#[derive(Debug)]
13pub struct GTTSClient {
14  /// The volume of the audio. Must be between 0.0 and 1.0. Default is 1.0.
15  ///
16  /// recommended value is 1.0
17  pub volume: f32,
18  /// The language of the gTTS client (ISO code)
19  ///
20  /// example: Languages::English, Languages::Japanese
21  pub language: Languages,
22  /// top-level domain of the gTTS client
23  ///
24  /// example: "com"
25  pub tld: &'static str,
26}
27pub enum Speed {
28  Normal,
29  Slow,
30}
31impl GTTSClient {
32  /// Creates a new gTTS client default values.
33  pub fn default() -> Self {
34    GTTSClient {
35      volume: 1.0,
36      language: Languages::English,
37      tld: "com",
38    }
39  }
40  /// Creates a new gTTS client with the given volume and language.
41  pub fn new(volume: f32, language: Languages, tld: &'static str) -> Self {
42    GTTSClient {
43      volume,
44      language,
45      tld,
46    }
47  }
48  pub fn save_to_file(&self, text: &str, filename: &str) -> Result<(), String> {
49    let len = text.len();
50    if len > GOOGLE_TTS_MAX_CHARS {
51      return Err(format!(
52        "The text is too long. Max length is {}",
53        GOOGLE_TTS_MAX_CHARS
54      ));
55    }
56    let language = Languages::as_code(self.language.clone());
57    let text = Core::fragmenter(text)?;
58    let rep = get(format!("https://translate.google.{}/translate_tts?ie=UTF-8&q={}&tl={}&total=1&idx=0&textlen={}&tl={}&client=tw-ob", self.tld, text.encoded, language, len, language))
59      .send()
60      .map_err(|e| format!("{}", e))?;
61    let mut file = File::create(filename).unwrap();
62    let bytes = rep.as_bytes();
63    if !bytes.is_empty() && file.write_all(bytes).is_ok() {
64      return Ok(());
65    }
66
67    Err("Something went wrong".to_string())
68  }
69  fn play_mp3(&self, mp3: &str) {
70    let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
71    let sink = rodio::Sink::try_new(&handle).unwrap();
72    sink.set_volume(self.volume);
73    let file = std::fs::File::open(mp3).unwrap();
74    sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
75    sink.sleep_until_end();
76  }
77  /// Speak the input according to the volume and language
78  pub fn speak(&self, input: &str) -> Result<(), String> {
79    self.save_to_file(input, "audio.mp3")?;
80    self.play_mp3("audio.mp3");
81    if Path::new("./audio.mp3").exists() {
82      fs::remove_file("./audio.mp3").unwrap();
83    }
84    Ok(())
85  }
86  /// Speak and println! the input according to the volume and language
87  pub fn display_and_speak(&self, input: &str) {
88    self.speak(input).unwrap();
89    println!("{}", input);
90  }
91  /// Fastest way to check if gTTS API works
92  pub fn test(&self) {
93    self.display_and_speak("Hello!");
94  }
95}
96#[cfg(test)]
97mod tests {
98  use super::*;
99
100  #[test]
101  fn test_1() {
102    let narrator = GTTSClient::default();
103    narrator.test();
104  }
105}