From 4b155248c69f3128c591fcf4e2c18db0eb460b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Tue, 3 Dec 2024 15:47:22 +0100 Subject: [PATCH] wip: add tts debug --- Makefile | 6 +++ pyproject.toml | 9 ++++- src/markovbot_gui/main.py | 28 ++++++++++++++ src/markovbot_gui/tts_window.py | 39 +++++++++++++++++++ uv.lock | 68 ++++++++++++++++++++++++++++++++- 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/markovbot_gui/tts_window.py diff --git a/Makefile b/Makefile index 7b1af53..daabaf9 100644 --- a/Makefile +++ b/Makefile @@ -7,3 +7,9 @@ tests: uv run pytest --cov=halig -vv tests --report-log reportlog.json uv run coverage html uv run coverage xml + +compile: + uv run pyinstaller markovbot.spec + +clean-compile: + uv run pyinstaller markovbot.spec --clean diff --git a/pyproject.toml b/pyproject.toml index 207f2de..3f95281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,11 @@ dependencies = [ "pyinstaller>=6.11.0", "twitchwebsocket>=1.2.1", "loguru>=0.7.2", + "ffmpeg>=1.4", + "ffmpeg-python>=0.2.0", + "simpleaudio>=1.0.4", + "pydub>=0.25.1", + "gtts>=2.5.4", ] [tool.uv] @@ -44,7 +49,9 @@ module = [ "nltk.tokenize.treebank", "nltk.tokenize.destructive", "TwitchWebsocket", - "tokenizer" + "tokenizer", + "simpleaudio", + "RealtimeTTS" ] ignore_missing_imports = true diff --git a/src/markovbot_gui/main.py b/src/markovbot_gui/main.py index d7ffdee..cad68b3 100644 --- a/src/markovbot_gui/main.py +++ b/src/markovbot_gui/main.py @@ -8,6 +8,7 @@ from kivy.uix.widget import Widget from src.markovbot_gui.bot_runner import BotRunner from src.markovbot_gui.config_window import ConfigWindow +from src.markovbot_gui.tts_window import TTSWindow class BotApp(App): @@ -51,6 +52,29 @@ class BotApp(App): popup.open() + def run_tts(self, instance): + """Create a TTS window and open it""" + tts_window = TTSWindow(config_path=self.config_path) + popup = Popup( + title="TTS testing ground", + content=tts_window, + size_hint=(None, None), + size=(dp(600), dp(400)), + auto_dismiss=False, + ) + + # Add close button + close_button = Button( + text="Close", + size_hint=(None, None), + size=(dp(100), dp(40)), + pos_hint={"center_x": 0.5}, + ) + close_button.bind(on_release=popup.dismiss) + tts_window.add_widget(close_button) + + popup.open() + def build(self): widget = Widget() @@ -60,6 +84,10 @@ class BotApp(App): run_button.bind(on_release=self.run_bot) layout.add_widget(run_button) + tts_button = Button(text="TTS button") + tts_button.bind(on_release=self.run_tts) + layout.add_widget(tts_button) + config_button = Button(text="Open config") config_button.bind(on_release=self.run_config) layout.add_widget(config_button) diff --git a/src/markovbot_gui/tts_window.py b/src/markovbot_gui/tts_window.py new file mode 100644 index 0000000..f16b937 --- /dev/null +++ b/src/markovbot_gui/tts_window.py @@ -0,0 +1,39 @@ +from pathlib import Path + +from kivy.metrics import dp +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.button import Button +from kivy.uix.textinput import TextInput + + +class TTSWindow(BoxLayout): + def __init__(self, config_path: Path, **kwargs): + super().__init__(**kwargs) + self.config_path = config_path + self.orientation = "vertical" + + self.spacing = dp(10) + self.padding = dp(20) + + # create a text input for the text to be converted to audio, + # and a button to execute the conversion + + text_layout = BoxLayout(orientation="vertical") + self.add_widget(text_layout) + + self.text_input = TextInput(multiline=True) + text_layout.add_widget(self.text_input) + + gtts_button = Button(text="GTTS") + gtts_button.bind(on_release=self.play) + text_layout.add_widget(gtts_button) + + def play(self, instance): + from gtts import gTTS + from pydub import AudioSegment + from pydub.playback import play + + tts = gTTS(self.text_input.text, lang="pt", tld="com.br") + tts.save("output.mp3") + audio = AudioSegment.from_mp3("output.mp3") + play(audio) diff --git a/uv.lock b/uv.lock index 3f9f0ad..ae14658 100644 --- a/uv.lock +++ b/uv.lock @@ -1,7 +1,8 @@ version = 1 requires-python = ">=3.11" resolution-markers = [ - "python_full_version < '3.13'", + "python_full_version < '3.12'", + "python_full_version == '3.12.*'", "python_full_version >= '3.13'", ] @@ -116,6 +117,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] +[[package]] +name = "ffmpeg" +version = "1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/cc/3b7408b8ecf7c1d20ad480c3eaed7619857bf1054b690226e906fdf14258/ffmpeg-1.4.tar.gz", hash = "sha256:6931692c890ff21d39938433c2189747815dca0c60ddc7f9bb97f199dba0b5b9", size = 5055 } + +[[package]] +name = "ffmpeg-python" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "future" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/5e/d5f9105d59c1325759d838af4e973695081fbbc97182baf73afc78dec266/ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127", size = 21543 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/0c/56be52741f75bad4dc6555991fabd2e07b432d333da82c11ad701123888a/ffmpeg_python-0.2.0-py3-none-any.whl", hash = "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5", size = 25024 }, +] + +[[package]] +name = "future" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326 }, +] + +[[package]] +name = "gtts" +version = "2.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/79/5ddb1dfcd663581d0d3fca34ccb1d8d841b47c22a24dc8dce416e3d87dfa/gtts-2.5.4.tar.gz", hash = "sha256:f5737b585f6442f677dbe8773424fd50697c75bdf3e36443585e30a8d48c1884", size = 24018 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/6c/8b8b1fdcaee7e268536f1bb00183a5894627726b54a9ddc6fc9909888447/gTTS-2.5.4-py3-none-any.whl", hash = "sha256:5dd579377f9f5546893bc26315ab1f846933dc27a054764b168f141065ca8436", size = 29184 }, +] + [[package]] name = "idna" version = "3.10" @@ -249,6 +290,9 @@ name = "markovbot-gui" version = "0.1.2" source = { virtual = "." } dependencies = [ + { name = "ffmpeg" }, + { name = "ffmpeg-python" }, + { name = "gtts" }, { name = "kivy", extra = ["base"] }, { name = "loguru" }, { name = "nltk" }, @@ -256,7 +300,9 @@ dependencies = [ { name = "platformdirs" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "pydub" }, { name = "pyinstaller" }, + { name = "simpleaudio" }, { name = "twitchwebsocket" }, ] @@ -269,6 +315,9 @@ dev = [ [package.metadata] requires-dist = [ + { name = "ffmpeg", specifier = ">=1.4" }, + { name = "ffmpeg-python", specifier = ">=0.2.0" }, + { name = "gtts", specifier = ">=2.5.4" }, { name = "kivy", extras = ["base"], specifier = ">=2.3.0" }, { name = "loguru", specifier = ">=0.7.2" }, { name = "nltk", specifier = ">=3.9.1" }, @@ -276,7 +325,9 @@ requires-dist = [ { name = "platformdirs", specifier = ">=4.3.6" }, { name = "pydantic", specifier = ">=2.9.2" }, { name = "pydantic-settings", specifier = ">=2.6.0" }, + { name = "pydub", specifier = ">=0.25.1" }, { name = "pyinstaller", specifier = ">=6.11.0" }, + { name = "simpleaudio", specifier = ">=1.0.4" }, { name = "twitchwebsocket", specifier = ">=1.2.1" }, ] @@ -490,6 +541,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/34/19/26bb6bdb9fdad5f0dfce538780814084fb667b4bc37fcb28459c14b8d3b5/pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0", size = 28578 }, ] +[[package]] +name = "pydub" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327 }, +] + [[package]] name = "pygments" version = "2.18.0" @@ -701,6 +761,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/31/2d/90165d51ecd38f9a02c6832198c13a4e48652485e2ccf863ebb942c531b6/setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8", size = 1249825 }, ] +[[package]] +name = "simpleaudio" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/1b/4dc29653733202b68c09d9c6ca085cf67ac54859ee860647ef21ac1ff3dc/simpleaudio-1.0.4.tar.gz", hash = "sha256:691c88649243544db717e7edf6a9831df112104e1aefb5f6038a5d071e8cf41d", size = 2042564 } + [[package]] name = "tqdm" version = "4.66.5"