diff --git a/.config.template.toml b/.config.template.toml index ddfa293..18f1b00 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -12,11 +12,11 @@ password = { optional = false, nmin = 8, explanation = "the password of your red random = { optional = true, options = [true, false, ], default = false, type = "bool", explanation = "If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: 'False'", example = "True" } -subreddit = { optional = false, regex = "[_0-9a-zA-Z]+$", nmin = 3, nmax = 21, explanation = "what subreddit to pull posts from, the name of the sub, not the URL", example = "AskReddit", oob_error = "A subreddit name HAS to be between 3 and 20 characters" } +subreddit = { optional = false, regex = "[_0-9a-zA-Z]+$", nmin = 3, explanation = "what subreddit to pull posts from, the name of the sub, not the URL", example = "AskReddit", oob_error = "A subreddit name HAS to be between 3 and 20 characters" } post_id = { optional = true, default = "", regex = "^((?!://|://)[+a-zA-Z])*$", explanation = "Used if you want to use a specific post.", example = "urdtfx" } max_comment_length = { default = 500, optional = false, nmin = 10, nmax = 10000, type = "int", explanation = "max number of characters a comment can have. default is 500", example = 500, oob_error = "the max comment length should be between 10 and 10000" } post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr" } - +min_comments = { default = 20, optional = false, nmin = 15, type = "int", explanation = "The minimum number of comments a post should have to be included. default is 20", example = 29, oob_error = "the minimum number of comments should be between 15 and 999999" } [settings] allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [true, false, @@ -29,10 +29,15 @@ opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets storymode = { optional = true, type = "bool", default = false, example = false, options = [true, false, ], explanation = "not yet implemented" } + +[settings.background] background_choice = { optional = true, default = "minecraft", example = "minecraft", options = ["minecraft", "gta", "rocket-league", "motor-gta", ""], explanation = "Sets the background for the video" } -background_audio = { optional = true, type = "bool", default = false, example = false, options = [true, - false, -], explaination="Sets a audio to play in the background (put a background.mp3 file in the assets/backgrounds directory for it to be used.)" } +#background_audio = { optional = true, type = "bool", default = false, example = false, options = [true, +# false, +#], explaination="Sets a audio to play in the background (put a background.mp3 file in the assets/backgrounds directory for it to be used.)" } +#background_audio_volume = { optional = true, type = "float", default = 0.3, example = 0.1, explanation="Sets the volume of the background audio. only used if the background_audio is also set to true" } + + [settings.tts] choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", ], example = "streamlabspolly", explanation = "The backend 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" } diff --git a/GUI/index.html b/GUI/index.html new file mode 100644 index 0000000..807c9e7 --- /dev/null +++ b/GUI/index.html @@ -0,0 +1,281 @@ + + + + + + RedditVideoMakerBot + + + + + + + + + +
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+ + + + + + + + + + + + diff --git a/README.md b/README.md index 57aa6d4..cb82738 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,5 @@ CallumIO (c.#6837) - https://github.com/CallumIO Verq (Verq#2338) - https://github.com/CordlessCoder LukaHietala (Pix.#0001) - https://github.com/LukaHietala + +Freebiell (Freebie#6429) - https://github.com/FreebieII diff --git a/TTS/aws_polly.py b/TTS/aws_polly.py index 13eaea1..efd762b 100644 --- a/TTS/aws_polly.py +++ b/TTS/aws_polly.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from boto3 import Session -from botocore.exceptions import BotoCoreError, ClientError +from botocore.exceptions import BotoCoreError, ClientError, ProfileNotFound import sys from utils import settings import random @@ -30,36 +30,46 @@ class AWSPolly: self.voices = voices def run(self, text, filepath, random_voice: bool = False): - session = Session(profile_name="polly") - polly = session.client("polly") - if random_voice: - voice = self.randomvoice() - else: - if not settings.config["settings"]["tts"]["aws_polly_voice"]: - return ValueError( - f"Please set the TOML variable AWS_VOICE to a valid voice. options are: {voices}" - ) - voice = str(settings.config["settings"]["tts"]["aws_polly_voice"]).capitalize() try: - # Request speech synthesis - response = polly.synthesize_speech( - Text=text, OutputFormat="mp3", VoiceId=voice, Engine="neural" - ) - except (BotoCoreError, ClientError) as error: - # The service returned an error, exit gracefully - print(error) - sys.exit(-1) + session = Session(profile_name="polly") + polly = session.client("polly") + if random_voice: + voice = self.randomvoice() + else: + if not settings.config["settings"]["tts"]["aws_polly_voice"]: + raise ValueError( + f"Please set the TOML variable AWS_VOICE to a valid voice. options are: {voices}" + ) + voice = str(settings.config["settings"]["tts"]["aws_polly_voice"]).capitalize() + try: + # Request speech synthesis + response = polly.synthesize_speech( + Text=text, OutputFormat="mp3", VoiceId=voice, Engine="neural" + ) + except (BotoCoreError, ClientError) as error: + # The service returned an error, exit gracefully + print(error) + sys.exit(-1) - # Access the audio stream from the response - if "AudioStream" in response: - file = open(filepath, "wb") - file.write(response["AudioStream"].read()) - file.close() - # print_substep(f"Saved Text {idx} to MP3 files successfully.", style="bold green") + # Access the audio stream from the response + if "AudioStream" in response: + file = open(filepath, "wb") + file.write(response["AudioStream"].read()) + file.close() + # print_substep(f"Saved Text {idx} to MP3 files successfully.", style="bold green") - else: - # The response didn't contain audio data, exit gracefully - print("Could not stream audio") + else: + # The response didn't contain audio data, exit gracefully + print("Could not stream audio") + sys.exit(-1) + except ProfileNotFound: + print("You need to install the AWS CLI and configure your profile") + print( + """ + Linux: https://docs.aws.amazon.com/polly/latest/dg/setup-aws-cli.html + Windows: https://docs.aws.amazon.com/polly/latest/dg/install-voice-plugin2.html + """ + ) sys.exit(-1) def randomvoice(self): diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index 1f27521..762aa47 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -82,12 +82,16 @@ class TTSEngine: r" *(((.|\n){0," + str(self.tts_module().max_chars) + "})(\.|.$))", text ) ] - - idy = None + offset = 0 for idy, text_cut in enumerate(split_text): # print(f"{idx}-{idy}: {text_cut}\n") - self.call_tts(f"{idx}-{idy}.part", text_cut) - split_files.append(AudioFileClip(f"{self.path}/{idx}-{idy}.part.mp3")) + if not text_cut or text_cut.isspace(): + offset += 1 + continue + + self.call_tts(f"{idx}-{idy - offset}.part", text_cut) + 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 ) diff --git a/TTS/streamlabs_polly.py b/TTS/streamlabs_polly.py index b7365ab..75c4f49 100644 --- a/TTS/streamlabs_polly.py +++ b/TTS/streamlabs_polly.py @@ -37,8 +37,8 @@ class StreamlabsPolly: voice = self.randomvoice() else: if not settings.config["settings"]["tts"]["streamlabs_polly_voice"]: - return ValueError( - f"Please set the config variable STREAMLABS_VOICE to a valid voice. options are: {voices}" + raise ValueError( + f"Please set the config variable STREAMLABS_POLLY_VOICE to a valid voice. options are: {voices}" ) voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize() body = {"voice": voice, "text": text, "service": "polly"} diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 31eb712..651e9a1 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -20,7 +20,7 @@ def get_subreddit_threads( print_substep("Logging into Reddit.") content = {} - if settings.config["reddit"]["creds"]["2fa"] == True: + if settings.config["reddit"]["creds"]["2fa"]: print("\nEnter your two-factor authentication code from your authenticator app.\n") code = input("> ") print() @@ -29,7 +29,7 @@ def get_subreddit_threads( else: passkey = settings.config["reddit"]["creds"]["password"] username = settings.config["reddit"]["creds"]["username"] - if username.casefold().startswith("u/"): + if str(username).casefold().startswith("u/"): username = username[2:] reddit = praw.Reddit( client_id=settings.config["reddit"]["creds"]["client_id"], @@ -57,7 +57,7 @@ def get_subreddit_threads( sub = settings.config["reddit"]["thread"]["subreddit"] print_substep(f"Using subreddit: r/{sub} from TOML config") subreddit_choice = sub - if subreddit_choice.casefold().startswith("r/"): # removes the r/ from the input + if str(subreddit_choice).casefold().startswith("r/"): # removes the r/ from the input subreddit_choice = subreddit_choice[2:] subreddit = reddit.subreddit( subreddit_choice @@ -67,11 +67,10 @@ def get_subreddit_threads( submission = reddit.submission(id=POST_ID) elif ( settings.config["reddit"]["thread"]["post_id"] - and len(settings.config["reddit"]["thread"]["post_id"].split("+")) == 1 + and len(str(settings.config["reddit"]["thread"]["post_id"]).split("+")) == 1 ): submission = reddit.submission(id=settings.config["reddit"]["thread"]["post_id"]) else: - threads = subreddit.hot(limit=25) submission = get_subreddit_undone(threads, subreddit) submission = check_done(submission) # double-checking @@ -107,6 +106,7 @@ def get_subreddit_threads( ): if ( top_level_comment.author is not None + and sanitize_text(top_level_comment.body) is not None ): # if errors occur with this change to if not. content["comments"].append( { diff --git a/requirements.txt b/requirements.txt index 10ce9c3..0dc26df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ moviepy==1.0.3 praw==7.6.0 pytube==12.1.0 requests==2.28.1 -rich==12.4.4 +rich==12.5.1 toml==0.10.2 translators==5.3.1 pyppeteer==1.0.2 diff --git a/utils/subreddit.py b/utils/subreddit.py index 48dceba..4eb0108 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -34,11 +34,16 @@ def get_subreddit_undone(submissions: list, subreddit): if submission.stickied: print_substep("This post was pinned by moderators. Skipping...") continue + if submission.num_comments <= int(settings.config["reddit"]["thread"]["min_comments"]): + print_substep( + f'This post has under the specified minimum of comments ({settings.config["reddit"]["thread"]["min_comments"]}). Skipping...' + ) + continue return submission print("all submissions have been done going by top submission order") return get_subreddit_undone( subreddit.top(time_filter="hour"), subreddit - ) # all of the videos in hot have already been done + ) # all the videos in hot have already been done def already_done(done_videos: list, submission) -> bool: diff --git a/utils/voice.py b/utils/voice.py index 4a77833..a0709fa 100644 --- a/utils/voice.py +++ b/utils/voice.py @@ -36,7 +36,7 @@ def sleep_until(time): # Convert datetime to unix timestamp and adjust for locality if isinstance(time, datetime): - # If we're on Python 3 and the user specified a timezone, convert to UTC and get tje timestamp. + # If we're on Python 3 and the user specified a timezone, convert to UTC and get the timestamp. if sys.version_info[0] >= 3 and time.tzinfo: end = time.astimezone(timezone.utc).timestamp() else: diff --git a/video_creation/background.py b/video_creation/background.py index 7ef4321..be0f46c 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -30,7 +30,7 @@ background_options = { "https://www.youtube.com/watch?v=2X9QGY__0II", "rocket_league.mp4", "Orbital Gameplay", - "top", + lambda t: ("center", 200 + t), ), "minecraft": ( # Minecraft parkour "https://www.youtube.com/watch?v=n_Dv4JMiwK8", @@ -64,7 +64,7 @@ def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int def get_background_config(): """Fetch the background/s configuration""" try: - choice = str(settings.config["settings"]["background_choice"]).casefold() + choice = str(settings.config["settings"]["background"]["background_choice"]).casefold() except AttributeError: print_substep("No background selected. Picking random background'") choice = None diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 6da3034..2dba251 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -5,8 +5,6 @@ import re from os.path import exists from typing import Tuple, Any -import translators as ts - from moviepy.editor import ( VideoFileClip, AudioFileClip, @@ -44,6 +42,8 @@ def name_normalize( lang = settings.config['reddit']['thread']['post_lang'] if lang: + import translators as ts + print_substep('Translating filename...') translated_name = ts.google(name, to_language=lang) return translated_name @@ -196,38 +196,13 @@ def make_final_video( verbose=False, threads=multiprocessing.cpu_count(), ) - if settings.config['settings']['background_audio']: - print('[bold green] Merging background audio with video') - if not exists('assets/backgrounds/background.mp3'): - print_substep( - 'Cannot find assets/backgrounds/background.mp3 audio file didn\'t so skipping.' - ) - ffmpeg_extract_subclip( - 'assets/temp/temp.mp4', - 0, - video_duration, - targetname=f'results/{subreddit}/{filename}', - ) - else: - ffmpeg_merge_video_audio( - 'assets/temp/temp.mp4', - 'assets/backgrounds/background.mp3', - 'assets/temp/temp_audio.mp4', - ) - ffmpeg_extract_subclip( # check if this gets run - 'assets/temp/temp_audio.mp4', - 0, - video_duration, - targetname=f'results/{subreddit}/{filename}', - ) - else: - print('debug duck') - ffmpeg_extract_subclip( - 'assets/temp/temp.mp4', - 0, - video_duration, - targetname=f'results/{subreddit}/{filename}', - ) + ffmpeg_extract_subclip( + "assets/temp/temp.mp4", + 0, + video_duration, + targetname=f'results/{subreddit}/{filename}', + ) + save_data(subreddit, filename, title, idx, background_config[2]) print_step('Removing temporary files 🗑') cleanups = cleanup() print_substep(f'Removed {cleanups} temporary files 🗑') diff --git a/video_creation/voices.py b/video_creation/voices.py index 0098da0..95f0b2b 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -29,7 +29,7 @@ def save_text_to_mp3( """ voice = settings.config['settings']['tts']['choice'] - if voice.casefold() not in map(lambda _: _.casefold(), TTSProviders): + if str(voice).casefold() not in map(lambda _: _.casefold(), TTSProviders): while True: print_step('Please choose one of the following TTS providers: ') print_table(TTSProviders)