def read_text(self, text: str, **config) -> None: """Reads out text.""" text = self.clean_text(text) text = TextParser.escape_tags(text) split_text = TextParser.split_text(text) rate = config.get('rate') volume = config.get('volume') voice = config.get('voice') assert voice, "Voice needs to be provided" # TODO: Does it? ssml = SSML(text, rate=rate, volume=volume) if self._cached_ssml == ssml and self._cached_voice == voice: self._logger.debug("Playing cached file") filepaths = self._cached_filepaths else: self._logger.debug("Re_cached_textquest from Polly") filepaths = [] # TODO: This should obviously be asynchronous! for idx, parted_text in enumerate(split_text): parted_ssml = SSML(parted_text, rate=rate, volume=volume) response = self.ask_polly(str(parted_ssml), voice) filename = create_filename(AbstractSpeaker.TMP_FILEPATH, idx) saved_filepath = save_mp3(response["AudioStream"].read(), filename) filepaths.append(saved_filepath) self.save_cache(ssml, filepaths, voice) self.play_files(filepaths) return
def test_split_text_above_3000_below_6000(self): document = 200 * self.sentece_24chars self.assertEqual(len(document), 4800, "Document should have 4800 chars length") split_text = list(TextParser.split_text(document)) self.assertEqual(len(split_text), 2, "Two parts")
def test_split_text_below_3000(self): document = 10 * self.sentece_24chars self.assertEqual(len(document), 240, "Document should have 240 chars lenght") split_text = TextParser.split_text(document) list_split_text = list(split_text) self.assertEqual(len(list_split_text), 1, "Only one part") self.assertEqual(list_split_text[0], document, "First part is the document")
def test_split_text_above_6000(self): document = "no dots in this text " * 300 # 21*300 = 6300 self.assertEqual(len(document), 6300, "Document should have 6300 chars length") split_text = list(TextParser.split_text(document)) self.assertEqual(len(split_text), 3, "Two parts") self.assertEqual(len(split_text[0]), len(split_text[1]), "Both parts should have the same length") self.assertEqual(len(split_text[2]), 300, "Simple maths: 6300 - 6000 = 300")
def __init__(self, app: QApplication): super().__init__() self.app = app self.config = Configuration() _ = self.config.read_config() self.player = QMediaPlayer() self.speaker: AbstractSpeaker = self.get_speaker( self.config.speaker, self.player) self.textParser = TextParser(config_path=self.config.parser_config) self.gui = MainWindow(self.config, speakers=self.SPEAKER) self.gui.speaker = self.speaker self.gui.player = self.player self.key_manager = KeyBoardManager(self.app) # Event on closing GUI application self.gui.closeAppEvent.connect(self._close) self._last_pid = None
class Cracker(object): """Logic for running the Cracker program""" SPEAKER = {Polly.__name__: Polly, Espeak.__name__: Espeak} _logger = logging.getLogger(__name__) def __init__(self, app: QApplication): super().__init__() self.app = app self.config = Configuration() _ = self.config.read_config() self.player = QMediaPlayer() self.speaker: AbstractSpeaker = self.get_speaker( self.config.speaker, self.player) self.textParser = TextParser(config_path=self.config.parser_config) self.gui = MainWindow(self.config, speakers=self.SPEAKER) self.gui.speaker = self.speaker self.gui.player = self.player self.key_manager = KeyBoardManager(self.app) # Event on closing GUI application self.gui.closeAppEvent.connect(self._close) self._last_pid = None def _close(self): "Handles closing whole application" self.key_manager.stop() def get_speaker(self, speaker_name, player) -> AbstractSpeaker: if speaker_name == Polly.__name__: if "profile_name" in self.config.default_values: profile_name = self.config.default_values["profile_name"] return Polly(player, profile_name) else: return Polly(player) elif speaker_name == Espeak.__name__: return Espeak(player) raise ValueError( f"No speaker was selected. Provided speaker name '{speaker_name}'") def run(self): self.gui.init() self.set_action() self.gui.show() def reduce_text(self): text = self.gui.textEdit.toPlainText() new_text = self.textParser.reduce_text(text) self.gui.textEdit.setText(new_text) def reduce_cite(self): text = self.gui.textEdit.toPlainText() new_text = self.textParser.reduce_cite(text) self.gui.textEdit.setText(new_text) def wiki_text(self): """Sets the text box with wikipedia specific cleaned text. Example of this is removing `citation needed` and other references. """ text = self.gui.textEdit.toPlainText() text = self.textParser.wiki_text(text) self.gui.textEdit.setText(text) def read_text_area(self): """Reads out text in the text_box with selected speaker.""" self.stop_text() text = self.gui.textEdit.toPlainText( ) # TODO: toHtml() gives more control self.textParser.parser_rules = self.config.regex_config text = self.textParser.reduce_text(text) self._read(text) def toggle_read_text_clipboard(self): """Reads out text from the clipboard with selected speaker.""" if self.player.state() == QMediaPlayer.PlayingState: self.stop_text() self.player.stop() else: self.stop_text() text = self.app.clipboard().text() self.textParser.parser_rules = self.config.regex_config text = self.textParser.reduce_text(text) self._read(text) def _read(self, text): speaker_config = self._prepare_config() self._last_pid = self.speaker.read_text(text, **speaker_config) def toggle_read(self): if self.player.state() == QMediaPlayer.PausedState: self.player.play() else: self.player.pause() def stop_text(self): self.speaker.stop_text() def _prepare_config(self): config = dict(rate=self.gui.rate, volume=self.gui.volume, voice=self.gui.config.voice) return config def change_speaker(self, speaker_name): """Action on changing speaker. Important: Each speaker has its own configuration. These values should be updated on change.""" self.speaker = self.SPEAKER[speaker_name](self.player) self.gui.change_speaker(speaker_name) def set_action(self): self.gui.stop_action.triggered.connect(self.stop_text) self.gui.read_action.triggered.connect(self.read_text_area) self.gui.clipboard_read_action.triggered.connect( self.toggle_read_text_clipboard) self.gui.toggle_action.triggered.connect(self.toggle_read) self.gui.reduce_action.triggered.connect(self.reduce_text) self.gui.wiki_action.triggered.connect(self.wiki_text) self.gui.speakerW.currentTextChanged.connect(self.change_speaker) self.key_manager.GlobalReadSignal.connect( self.toggle_read_text_clipboard) args = (["space", "control", "shift"], ) p = Thread(target=self.key_manager.run, args=args) p.start()
def test_escape_tags_xml(self): s = '<he><said>She said</said></he>' out_s = TextParser.escape_tags(s) expected_s = '<he><said>She said</said></he>' self.assertEqual(out_s, expected_s, "Tags should be converted to &...;")
def test_escape_char_quote(self): s = 'He said "she said"' out_s = TextParser.escape_tags(s) self.assertEqual(out_s, s, "Quotes shouldn't be changed")
def test_split_text_generator_type(self): document = self.sentece_24chars split_text = TextParser.split_text(document) self.assertIsInstance(split_text, types.GeneratorType, "Return generator type")
def test_paper_two_square_brackets(self): paper_text = "It's hard to argue with this [Smartguy, 1003; Follower, 1004]" expected_text = "It's hard to argue with this " reduced_text = TextParser.reduce_cite(paper_text) self.assertEqual(reduced_text, expected_text)
def test_paper_single_square_brackets(self): paper_text = "Someone said something [Smartguy, 1002]" expected_text = "Someone said something " reduced_text = TextParser.reduce_cite(paper_text) self.assertEqual(expected_text, reduced_text)
def test_paper_single_round_brackets(self): paper_text = "Someone said something (Smartguy, 1002)" expected_text = "Someone said something " reduced_text = TextParser.reduce_cite(paper_text) self.assertEqual(reduced_text, expected_text)