From c58efb23fc30d64bca7a1eb6b5615101b00eb829 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:40:33 -0400 Subject: [PATCH 1/7] think I fixed polly needs testing tho --- TTS/POLLY.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ TTS/TikTok.py | 4 ++ TTS/swapper.py | 3 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 TTS/POLLY.py diff --git a/TTS/POLLY.py b/TTS/POLLY.py new file mode 100644 index 0000000..79a6f91 --- /dev/null +++ b/TTS/POLLY.py @@ -0,0 +1,115 @@ +import os +import random +import re + +import requests +import sox +from moviepy.audio.AudioClip import concatenate_audioclips, CompositeAudioClip +from moviepy.audio.io.AudioFileClip import AudioFileClip +voices = {'neural': [ + 'Ivy', + 'Joanna', + 'Kendra', + 'Kimberly', + 'Salli', + 'Joey', + 'Justin', + 'Matthew', + 'Amy', + 'Emma', + 'Brian' + +], 'standard': [ + 'Ivy', + 'Joanna', + 'Kendra', + 'Kimberly', + 'Salli', + 'Joey', + 'Justin', + 'Matthew', + "Russell", + "Nicole", + "Amy", + "Emma", + "Brian", + "Aditi", + "Raveena", + "Geraint" +]} + + +# valid voices https://lazypy.ro/tts/ + + +class POLLY: + def __init__(self): + self.url = 'https://streamlabs.com/polly/speak' + + def tts( + self, + req_text: str = "Amazon Text To Speech", + filename: str = "title.mp3", + random_speaker=False, + censer=False, + ): + if random_speaker: + voice = self.randomvoice() + else: + if not os.getenv('VOICE'): + return ValueError('Please set the environment variable VOICE to a valid voice. options are: {}'.format(voices)) + voice = (os.getenv("VOICE")) + body = {'voice': voice, 'text': req_text} + response = requests.post(self.url, data=body) + try: + voice_data = requests.get(response.json()['speak_url']) + with open(filename, 'wb') as f: + f.write(voice_data.content) + except KeyError: + if response.json()['error'] == 'Text length is too long!': + chunks = [ + m.group().strip() for m in re.finditer(r" *((.{0,299})(\.|.$))", req_text) + ] + + audio_clips = [] + cbn = sox.Combiner() + + chunkId = 0 + for chunk in chunks: + body = {'voice': 'Brian', 'text': chunk} + resp = requests.post(self.url, data=body) + voice_data = requests.get(resp.json()['speak_url']) + with open(filename.replace(".mp3", f"-{chunkId}.mp3"), "wb") as out: + out.write(voice_data.content) + + audio_clips.append(filename.replace(".mp3", f"-{chunkId}.mp3")) + + chunkId = chunkId + 1 + try: + if len(audio_clips) > 1: + cbn.convert(samplerate=44100, n_channels=2) + cbn.build(audio_clips, filename, "concatenate") + else: + os.rename(audio_clips[0], filename) + except (sox.core.SoxError, + FileNotFoundError): # https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/67#issuecomment-1150466339 + for clip in audio_clips: + i = audio_clips.index(clip) # get the index of the clip + audio_clips = ( + audio_clips[:i] + [AudioFileClip(clip)] + audio_clips[i + 1:] + ) # replace the clip with an AudioFileClip + audio_concat = concatenate_audioclips(audio_clips) + audio_composite = CompositeAudioClip([audio_concat]) + audio_composite.write_audiofile(filename, 44100, 2, 2000, None) + + def make_readable(self, text): + """ + Amazon Polly fails to read some symbols properly such as '& (and)'. + So we normalize input text before passing it to the service + """ + text = text.replace('&', 'and') + return text + + def randomvoice(self): + valid = voices['neural'] + voices['standard'] + return random.choice(valid) diff --git a/TTS/TikTok.py b/TTS/TikTok.py index 8ed9f8e..79d472a 100644 --- a/TTS/TikTok.py +++ b/TTS/TikTok.py @@ -110,6 +110,7 @@ class TikTok: # TikTok Text-to-Speech Wrapper r = session.post( f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0" ) + print(r.text) vstr = [r.json()["data"]["v_str"]][0] b64d = base64.b64decode(vstr) @@ -141,3 +142,6 @@ class TikTok: # TikTok Text-to-Speech Wrapper if ok_or_good == 1: # 1/10 chance of ok voice return random.choice(voices) return random.choice(human) # 9/10 chance of good voice + + +TikTok().tts('Hello World', '../TTS/hello.mp3') diff --git a/TTS/swapper.py b/TTS/swapper.py index cd18223..f4717b1 100644 --- a/TTS/swapper.py +++ b/TTS/swapper.py @@ -3,9 +3,10 @@ from os import getenv from dotenv import load_dotenv from TTS.GTTS import GTTS +from TTS.POLLY import POLLY from TTS.TikTok import TikTok -CHOICE_DIR = {"tiktok": TikTok, "gtts": GTTS} +CHOICE_DIR = {"tiktok": TikTok, "gtts": GTTS, 'polly': POLLY} class TTS: From ebe6974cc5c5d0e7ae1f9da11b069cb67738d0d6 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:44:22 -0400 Subject: [PATCH 2/7] left debug thing. --- .env.template | 4 ++-- TTS/TikTok.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.env.template b/.env.template index b66ec3e..d5e82fd 100644 --- a/.env.template +++ b/.env.template @@ -32,8 +32,8 @@ MAX_COMMENT_LENGTH="500" OPACITY="1" # see TTSwrapper.py for all valid options -VOICE="en_us_001" # e.g. en_us_002 +VOICE="" # e.g. en_us_002 # IN-PROGRESS - not yet implemented -TTsChoice="TikTok" # todo add docs +TTsChoice="polly" # todo add docs STORYMODE="False" diff --git a/TTS/TikTok.py b/TTS/TikTok.py index 79d472a..a299216 100644 --- a/TTS/TikTok.py +++ b/TTS/TikTok.py @@ -142,6 +142,3 @@ class TikTok: # TikTok Text-to-Speech Wrapper if ok_or_good == 1: # 1/10 chance of ok voice return random.choice(voices) return random.choice(human) # 9/10 chance of good voice - - -TikTok().tts('Hello World', '../TTS/hello.mp3') From c3279f7995bade66837ea9001eada77b7ad50b9d Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:45:48 -0400 Subject: [PATCH 3/7] removed tts choice from debug --- .env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.template b/.env.template index d5e82fd..bb9dced 100644 --- a/.env.template +++ b/.env.template @@ -33,7 +33,7 @@ OPACITY="1" # see TTSwrapper.py for all valid options VOICE="" # e.g. en_us_002 +TTsChoice="polly" # todo add docs # IN-PROGRESS - not yet implemented -TTsChoice="polly" # todo add docs STORYMODE="False" From 045d7b2b011acf80551488a3f0101c625de6564d Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:49:24 -0400 Subject: [PATCH 4/7] fixed capitalization issue --- .env.template | 2 +- TTS/POLLY.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index bb9dced..6bf7e4f 100644 --- a/.env.template +++ b/.env.template @@ -32,7 +32,7 @@ MAX_COMMENT_LENGTH="500" OPACITY="1" # see TTSwrapper.py for all valid options -VOICE="" # e.g. en_us_002 +VOICE="Matthew" # e.g. en_us_002 TTsChoice="polly" # todo add docs # IN-PROGRESS - not yet implemented diff --git a/TTS/POLLY.py b/TTS/POLLY.py index 79a6f91..360f98f 100644 --- a/TTS/POLLY.py +++ b/TTS/POLLY.py @@ -58,7 +58,7 @@ class POLLY: else: if not os.getenv('VOICE'): return ValueError('Please set the environment variable VOICE to a valid voice. options are: {}'.format(voices)) - voice = (os.getenv("VOICE")) + voice = str(os.getenv("VOICE")).capitalize() body = {'voice': voice, 'text': req_text} response = requests.post(self.url, data=body) try: From e96a4a5859af321c6771959b5239b7798153d5db Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:50:03 -0400 Subject: [PATCH 5/7] changed regex splitter --- TTS/POLLY.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TTS/POLLY.py b/TTS/POLLY.py index 360f98f..4696508 100644 --- a/TTS/POLLY.py +++ b/TTS/POLLY.py @@ -68,7 +68,7 @@ class POLLY: except KeyError: if response.json()['error'] == 'Text length is too long!': chunks = [ - m.group().strip() for m in re.finditer(r" *((.{0,299})(\.|.$))", req_text) + m.group().strip() for m in re.finditer(r" *((.{0,540})(\.|.$))", req_text) ] audio_clips = [] From bb2e02288fb15b3d3e426036a9369ed7ca080b48 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 17:51:42 -0400 Subject: [PATCH 6/7] changed regex splitter --- TTS/POLLY.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TTS/POLLY.py b/TTS/POLLY.py index 4696508..a32510f 100644 --- a/TTS/POLLY.py +++ b/TTS/POLLY.py @@ -68,7 +68,7 @@ class POLLY: except KeyError: if response.json()['error'] == 'Text length is too long!': chunks = [ - m.group().strip() for m in re.finditer(r" *((.{0,540})(\.|.$))", req_text) + m.group().strip() for m in re.finditer(r" *((.{0,530})(\.|.$))", req_text) ] audio_clips = [] From d9015df8ed6cc162915ebe074f567592c20fb523 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Jun 2022 18:00:44 -0400 Subject: [PATCH 7/7] fixed shirts i think --- video_creation/background.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/video_creation/background.py b/video_creation/background.py index e8f8ad1..d1a7948 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -14,7 +14,7 @@ def get_start_and_end_times(video_length, length_of_clip): def download_background(): - """Downloads the backgrounds/s video from youtube.""" + """Downloads the backgrounds/s video from YouTube.""" Path("./assets/backgrounds/").mkdir(parents=True, exist_ok=True) background_options = [ # uri , filename , credit ("https://www.youtube.com/watch?v=n_Dv4JMiwK8", "parkour.mp4", "bbswitzer"), @@ -25,7 +25,7 @@ def download_background(): # ), ] # note: make sure the file name doesn't include an - in it - if not len(listdir("./assets/backgrounds")) <= len( + if not len(listdir("./assets/backgrounds")) >= len( background_options ): # if there are any background videos not installed print_step(