diff --git a/GUI.py b/GUI.py new file mode 100644 index 0000000..90e37ae --- /dev/null +++ b/GUI.py @@ -0,0 +1,30 @@ +# Import the server module +import http.server +import webbrowser +# Set the hostname +HOST = "localhost" +# Set the port number +PORT = 4000 + +# Define class to display the index page of the web server +class PythonServer(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + if self.path == '/GUI': + self.path = 'index.html' + return http.server.SimpleHTTPRequestHandler.do_GET(self) + +# Declare object of the class +webServer = http.server.HTTPServer((HOST, PORT), PythonServer) +# Print the URL of the webserver, new =2 opens in a new tab +print(f"Server started at http://{HOST}:{PORT}/GUI/") +webbrowser.open(f'http://{HOST}:{PORT}/GUI/', new = 2) +print("Website opened in new tab") +print("Press Ctrl+C to quit") +try: + # Run the web server + webServer.serve_forever() +except KeyboardInterrupt: + # Stop the web server + webServer.server_close() + print("The server is stopped.") + exit() \ No newline at end of file diff --git a/GUI/index.html b/GUI/index.html index 807c9e7..c771adc 100644 --- a/GUI/index.html +++ b/GUI/index.html @@ -149,7 +149,7 @@ video += '
'; video += '
'; video += 'View'; - video += 'Download'; + video += 'Download'; video += '
'; video += '
'; video += ''; diff --git a/README.md b/README.md index d7227f9..8d61d9a 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ This can also be used to update the installation (Note if you got an error installing or running the bot try first rerunning the command with a three after the name e.g. python3 or pip3) +If you want to read more detailed guide about the bot, please refer to the [documentation](https://luka-hietala.gitbook.io/documentation-for-the-reddit-bot/) + ## Video https://user-images.githubusercontent.com/66544866/173453972-6526e4e6-c6ef-41c5-ab40-5d275e724e7c.mp4 diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index d09c117..12668df 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -60,16 +60,11 @@ class TTSEngine: self.call_tts("title", process_text(self.reddit_object["thread_title"])) processed_text = process_text(self.reddit_object["thread_post"]) - if ( - processed_text != "" - and settings.config["settings"]["storymode"] == True - ): + if processed_text != "" and settings.config["settings"]["storymode"] == True: self.call_tts("posttext", processed_text) idx = None - for idx, comment in track( - enumerate(self.reddit_object["comments"]), "Saving..." - ): + for idx, comment in track(enumerate(self.reddit_object["comments"]), "Saving..."): # ! Stop creating mp3 files if the length is greater than max length. if self.length > self.max_length: self.length -= self.last_clip_length @@ -96,15 +91,13 @@ class TTSEngine: offset = 0 for idy, text_cut in enumerate(split_text): # print(f"{idx}-{idy}: {text_cut}\n") - new_text = process_text(text_cut) + new_text = process_text(text_cut) if not new_text or new_text.isspace(): offset += 1 continue self.call_tts(f"{idx}-{idy - offset}.part", new_text) - split_files.append( - AudioFileClip(f"{self.path}/{idx}-{idy - offset}.part.mp3") - ) + split_files.append(AudioFileClip(f"{self.path}/{idx}-{idy - offset}.part.mp3")) CompositeAudioClip([concatenate_audioclips(split_files)]).write_audiofile( f"{self.path}/{idx}.mp3", fps=44100, verbose=False, logger=None @@ -121,17 +114,14 @@ class TTSEngine: # Path(f"{self.path}/{idx}-{i}.part.mp3").unlink() def call_tts(self, filename: str, text: str): - self.tts_module.run( - text, filepath=f"{self.path}/{filename}.mp3" - ) + self.tts_module.run(text, filepath=f"{self.path}/{filename}.mp3") # try: # self.length += MP3(f"{self.path}/{filename}.mp3").info.length # except (MutagenError, HeaderNotFoundError): # self.length += sox.file_info.duration(f"{self.path}/{filename}.mp3") try: clip = AudioFileClip(f"{self.path}/{filename}.mp3") - if clip.duration + self.length < self.max_length: - self.last_clip_length = clip.duration + self.last_clip_length = clip.duration self.length += clip.duration clip.close() except: diff --git a/TTS/pyttsx.py b/TTS/pyttsx.py new file mode 100644 index 0000000..06d27c5 --- /dev/null +++ b/TTS/pyttsx.py @@ -0,0 +1,42 @@ +import random +import pyttsx3 +from utils import settings + + +class pyttsx: + def __init__(self): + self.max_chars = 5000 + self.voices = [] + + def run( + self, + text: str, + filepath: str, + random_voice=False, + ): + voice_id = settings.config["settings"]["tts"]["python_voice"] + voice_num = settings.config["settings"]["tts"]["py_voice_num"] + if voice_id == "" or voice_num == "": + voice_id = 2 + voice_num = 3 + raise ValueError( + "set pyttsx values to a valid value, switching to defaults" + ) + else: + voice_id = int(voice_id) + voice_num = int(voice_num) + for i in range(voice_num): + self.voices.append(i) + i = +1 + if random_voice: + voice_id = self.randomvoice() + engine = pyttsx3.init() + voices = engine.getProperty("voices") + engine.setProperty( + "voice", voices[voice_id].id + ) # changing index changes voices but ony 0 and 1 are working here + engine.save_to_file(text, f"{filepath}") + engine.runAndWait() + + def randomvoice(self): + return random.choice(self.voices) diff --git a/main.py b/main.py index 2ea08e3..7e0ef02 100755 --- a/main.py +++ b/main.py @@ -11,6 +11,7 @@ from utils.cleanup import cleanup from utils.console import print_markdown, print_step, print_substep from utils import settings from utils.id import id +from utils.version import checkversion from video_creation.background import ( download_background, @@ -21,8 +22,7 @@ from video_creation.final_video import make_final_video from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts from video_creation.voices import save_text_to_mp3 -__VERSION__ = "2.3.1" -__BRANCH__ = "develop" +__VERSION__ = "2.4.1" print( """ @@ -38,8 +38,7 @@ print( print_markdown( "### Thanks for using this tool! [Feel free to contribute to this project on GitHub!](https://lewismenelaws.com) If you have any questions, feel free to reach out to me on Twitter or submit a GitHub issue. You can find solutions to many common problems in the [Documentation](https://luka-hietala.gitbook.io/documentation-for-the-reddit-bot/)" ) -print_step(f"You are using v{__VERSION__} of the bot in the {__BRANCH__} branch.") - +checkversion(__VERSION__) def main(POST_ID=None): reddit_object = get_subreddit_threads(POST_ID) diff --git a/ptt.py b/ptt.py new file mode 100644 index 0000000..6b49ef6 --- /dev/null +++ b/ptt.py @@ -0,0 +1,10 @@ +import pyttsx3 + +engine = pyttsx3.init() +voices = engine.getProperty("voices") +for voice in voices: + print(voice, voice.id) + engine.setProperty("voice", voice.id) + engine.say("Hello World!") + engine.runAndWait() + engine.stop() diff --git a/requirements.txt b/requirements.txt index 7bccd0d..a954c20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,5 +9,5 @@ requests==2.28.1 rich==12.5.1 toml==0.10.2 translators==5.3.1 - +pyttsx3==2.90 Pillow~=9.1.1 diff --git a/utils/.config.template.toml b/utils/.config.template.toml index 010a29b..31ae9aa 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -31,7 +31,9 @@ background_choice = { optional = true, default = "minecraft", example = "rocket- [settings.tts] -voice_choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", ], example = "tiktok", explanation = "The voice platform used for TTS generation. This can be left blank and you will be prompted to choose at runtime." } +voice_choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx",], example = "tiktok", explanation = "The voice platform used for TTS generation. This can be left blank and you will be prompted to choose at runtime." } aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" } streamlabs_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for Streamlabs Polly" } tiktok_voice = { optional = false, default = "en_us_006", example = "en_us_006", explanation = "The voice used for TikTok TTS" } +python_voice = {optional = false, default = "1", example = "1", explanation = "The index of the system tts voices (can be downloaded externally, run ptt.py to find value, start from zero)"} +py_voice_num = {optional = false, default = "2", example = "2", explanation= "the number of system voices(2 are pre-installed in windows)"} diff --git a/utils/console.py b/utils/console.py index 6f99a41..1518c27 100644 --- a/utils/console.py +++ b/utils/console.py @@ -118,3 +118,4 @@ def handle_input( console.print( "[red bold]" + err_message + "\nValid options are: " + ", ".join(map(str, options)) + "." ) + diff --git a/utils/version.py b/utils/version.py new file mode 100644 index 0000000..3042c0c --- /dev/null +++ b/utils/version.py @@ -0,0 +1,11 @@ +import requests +from utils.console import print_step + +def checkversion(__VERSION__): + response = requests.get("https://api.github.com/repos/elebumm/RedditVideoMakerBot/releases/latest") + latestversion = (response.json()["tag_name"]) + if __VERSION__ == latestversion: + print_step(f"You are using the newest version ({__VERSION__}) of the bot") + return True + else: + print_step(f"You are using an older version ({__VERSION__}) of the bot. Download the newest version ({latestversion}) from https://github.com/elebumm/RedditVideoMakerBot/releases/latest") \ No newline at end of file diff --git a/video_creation/data/videos.json b/video_creation/data/videos.json index fe51488..0637a08 100644 --- a/video_creation/data/videos.json +++ b/video_creation/data/videos.json @@ -1 +1 @@ -[] +[] \ No newline at end of file diff --git a/video_creation/final_video.py b/video_creation/final_video.py index c2d7a18..743e561 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -126,7 +126,7 @@ def make_final_video( title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"]) idx = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) - filename = f"{name_normalize(title)}.mp4" + filename = f"{name_normalize(title)[:251]}.mp4" subreddit = settings.config["reddit"]["thread"]["subreddit"] if not exists(f"./results/{subreddit}"): diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index 0542c49..2344fce 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -50,7 +50,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in print_substep("Post is NSFW. You are spicy...") page.locator('[data-testid="content-gate"] button').click() - page.wait_for_load_state() # Wait for page to fully load + page.wait_for_load_state() # Wait for page to fully load if page.locator('[data-click-id="text"] button').is_visible(): page.locator( diff --git a/video_creation/voices.py b/video_creation/voices.py index 066921c..f49514d 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -9,6 +9,7 @@ from TTS.GTTS import GTTS from TTS.streamlabs_polly import StreamlabsPolly from TTS.aws_polly import AWSPolly from TTS.TikTok import TikTok +from TTS.pyttsx import pyttsx from utils import settings from utils.console import print_table, print_step @@ -20,6 +21,7 @@ TTSProviders = { "AWSPolly": AWSPolly, "StreamlabsPolly": StreamlabsPolly, "TikTok": TikTok, + "pyttsx": pyttsx, }