diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index ca297e1..c010f56 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -69,7 +69,8 @@ class TTSEngine: else: self.split_post(comment["comment_body"], idx) - print_substep("Saved Text to MP3 files successfully.", style="bold green") + print_substep("Saved Text to MP3 files successfully.", + style="bold green") return self.length, idx def split_post(self, text: str, idx: int) -> str: @@ -85,7 +86,8 @@ class TTSEngine: 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")) + split_files.append(AudioFileClip( + f"{self.path}/{idx}-{idy}.part.mp3")) CompositeAudioClip([concatenate_audioclips(split_files)]).write_audiofile( f"{self.path}/{idx}.mp3", fps=44100, verbose=False, logger=None ) diff --git a/cli.py b/cli.py index 893e3c2..bf79a14 100644 --- a/cli.py +++ b/cli.py @@ -12,7 +12,7 @@ def program_options(): """ parser = argparse.ArgumentParser( - prog="RedditVideoMakerBot", # can be renamed, just a base + prog="RedditVideoMakerBot", # can be renamed, just a base usage="RedditVideoMakerBot [OPTIONS]", description=description ) @@ -21,7 +21,7 @@ def program_options(): help="Create a video (uses the defaults).", action="store_true" ) - parser.add_argument( # only accepts the name of subreddit, not links. + parser.add_argument( # only accepts the name of subreddit, not links. "-s", "--subreddit", help="Specify a subreddit.", action="store" @@ -65,7 +65,8 @@ def program_options(): args.number, ) if not create: - try_again = input("Something went wrong! Try again? [y/N] > ").strip() + try_again = input( + "Something went wrong! Try again? [y/N] > ").strip() if try_again in ["y", "Y"]: continue diff --git a/main.py b/main.py index 863a4bc..7f5de77 100755 --- a/main.py +++ b/main.py @@ -32,29 +32,27 @@ print_markdown( def main( - subreddit_=None, - background=None, - filename=None, - thread_link_=None, - number_of_comments=None - ): + subreddit_=None, + background=None, + filename=None, + thread_link_=None, + number_of_comments=None +): if check_env() is not True: exit() load_dotenv() cleanup() - - reddit_object = get_subreddit_threads( - subreddit_, - thread_link_, - number_of_comments - ) + subreddit_, + thread_link_, + number_of_comments + ) length, number_of_comments = save_text_to_mp3(reddit_object) download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_background(background) chop_background_video(length) - make_final_video(number_of_comments, length,filename) + make_final_video(number_of_comments, length, filename) def run_many(times): diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 626fdad..bb29610 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -1,3 +1,10 @@ +from dotenv import load_dotenv +from prawcore.exceptions import ( + OAuthException, + ResponseException, + RequestException, + BadRequest +) import random import os import re @@ -10,7 +17,8 @@ from utils.subreddit import get_subreddit_undone from utils.videos import check_done from praw.models import MoreComments -TEXT_WHITELIST = set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890") +TEXT_WHITELIST = set( + "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890") def textify(text): @@ -24,17 +32,6 @@ def try_env(param, backup): return backup -from prawcore.exceptions import ( - OAuthException, - ResponseException, - RequestException, - BadRequest -) -from dotenv import load_dotenv - -from utils.console import print_step, print_substep - - def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): """ Takes subreddit_ as parameter which defaults to None, but in this @@ -67,11 +64,11 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): password=passkey.strip(), ) except ( - OAuthException, - ResponseException, - RequestException, - BadRequest - ): + OAuthException, + ResponseException, + RequestException, + BadRequest + ): print_substep( "[bold red]There is something wrong with the .env file, kindly check:[/bold red]\n" + "1. ClientID\n" @@ -80,7 +77,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): + "4. Check if the type of Reddit app created is script (personal use script)." ) - # If the user specifies that he doesnt want a random thread, or if # he doesn't insert the "RANDOM_THREAD" variable at all, ask the thread link while True: @@ -136,7 +132,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): + f"And has a {num_comments}[/blue].\n" ) - try: content["thread_url"] = submission.url content["thread_title"] = submission.title @@ -160,7 +155,8 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): except AttributeError: pass - print_substep("AskReddit threads retrieved successfully.", style="bold green") + print_substep("AskReddit threads retrieved successfully.", + style="bold green") content["thread_url"] = f"https://reddit.com{submission.permalink}" content["thread_title"] = submission.title @@ -180,5 +176,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): "comment_id": top_level_comment.id, } ) - print_substep("Received subreddit threads Successfully.", style="bold green") + print_substep("Received subreddit threads Successfully.", + style="bold green") return content diff --git a/setup.py b/setup.py index 6063e5a..a2234e7 100755 --- a/setup.py +++ b/setup.py @@ -108,7 +108,8 @@ user = handle_input( 20, "A username HAS to be between 3 and 20 characters", ) -passw = handle_input("Password > ", False, ".*", "", 8, None, "Password too short") +passw = handle_input("Password > ", False, ".*", "", + 8, None, "Password too short") twofactor = handle_input( "2fa Enabled? (yes/no) > ", False, diff --git a/utils/checker.py b/utils/checker.py index 668b40f..efc3e6b 100755 --- a/utils/checker.py +++ b/utils/checker.py @@ -15,9 +15,10 @@ def check_env() -> bool: Returns: bool: Whether or not everything was put in properly - """ + """ if not os.path.exists(".env.template"): - console.print("[red]Couldn't find .env.template. Unable to check variables.") + console.print( + "[red]Couldn't find .env.template. Unable to check variables.") return True if not os.path.exists(".env"): console.print("[red]Couldn't find the .env file, creating one now.") @@ -38,14 +39,17 @@ def check_env() -> bool: if line.startswith("#") is not True and "=" in line and var_optional is not True: req_envs.append(line.split("=")[0]) if "#" in line: - examples[line.split("=")[0]] = "#".join(line.split("#")[1:]).strip() + examples[line.split("=")[0]] = "#".join( + line.split("#")[1:]).strip() elif "#OPTIONAL" in line: var_optional = True elif line.startswith("#MATCH_REGEX "): - matching[req_envs[-1]] = line.removeprefix("#MATCH_REGEX ")[:-1] + matching[req_envs[-1] + ] = line.removeprefix("#MATCH_REGEX ")[:-1] var_optional = False elif line.startswith("#OOB_ERROR "): - oob_errors[req_envs[-1]] = line.removeprefix("#OOB_ERROR ")[:-1] + oob_errors[req_envs[-1] + ] = line.removeprefix("#OOB_ERROR ")[:-1] var_optional = False elif line.startswith("#RANGE "): bounds[req_envs[-1]] = tuple( @@ -56,10 +60,12 @@ def check_env() -> bool: ) var_optional = False elif line.startswith("#MATCH_TYPE "): - types[req_envs[-1]] = eval(line.removeprefix("#MATCH_TYPE ")[:-1].split()[0]) + types[req_envs[-1] + ] = eval(line.removeprefix("#MATCH_TYPE ")[:-1].split()[0]) var_optional = False elif line.startswith("#EXPLANATION "): - explanations[req_envs[-1]] = line.removeprefix("#EXPLANATION ")[:-1] + explanations[req_envs[-1] + ] = line.removeprefix("#EXPLANATION ")[:-1] var_optional = False else: var_optional = False @@ -99,17 +105,22 @@ def check_env() -> bool: title_justify="left", title_style="#C0CAF5 bold", ) - table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) - table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) - table.add_column("Example", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Variable", justify="left", + style="#7AA2F7 bold", no_wrap=True) + table.add_column("Explanation", justify="left", + style="#BB9AF7", no_wrap=False) + table.add_column("Example", justify="center", + style="#F7768E", no_wrap=True) table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) for env in missing: table.add_row( env, - explanations[env] if env in explanations.keys() else "No explanation given", + explanations[env] if env in explanations.keys( + ) else "No explanation given", examples[env] if env in examples.keys() else "", - str(bounds[env][0]) if env in bounds.keys() and bounds[env][1] is not None else "", + str(bounds[env][0]) if env in bounds.keys( + ) and bounds[env][1] is not None else "", str(bounds[env][1]) if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None else "", @@ -127,20 +138,26 @@ def check_env() -> bool: title_justify="left", title_style="#C0CAF5 bold", ) - table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) - table.add_column("Current value", justify="left", style="#F7768E", no_wrap=False) - table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) - table.add_column("Example", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Variable", justify="left", + style="#7AA2F7 bold", no_wrap=True) + table.add_column("Current value", justify="left", + style="#F7768E", no_wrap=False) + table.add_column("Explanation", justify="left", + style="#BB9AF7", no_wrap=False) + table.add_column("Example", justify="center", + style="#F7768E", no_wrap=True) table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) for env in incorrect: table.add_row( env, os.getenv(env), - explanations[env] if env in explanations.keys() else "No explanation given", + explanations[env] if env in explanations.keys( + ) else "No explanation given", str(types[env].__name__) if env in types.keys() else "str", str(bounds[env][0]) if env in bounds.keys() else "None", - str(bounds[env][1]) if env in bounds.keys() and len(bounds[env]) > 1 else "None", + str(bounds[env][1]) if env in bounds.keys() and len( + bounds[env]) > 1 else "None", ) missing.add(env) console.print(table) @@ -157,11 +174,13 @@ def check_env() -> bool: with open(".env", "r+") as env_file: lines = [] for line in env_file.readlines(): - line.split("=")[0].strip() not in incorrect and lines.append(line) + line.split("=")[0].strip( + ) not in incorrect and lines.append(line) env_file.seek(0) env_file.write("\n".join(lines)) env_file.truncate() - console.print("[green]Successfully removed incorrectly set variables from .env") + console.print( + "[green]Successfully removed incorrectly set variables from .env") with open(".env", "a") as env_file: for env in missing: env_file.write( @@ -177,11 +196,14 @@ def check_env() -> bool: if env in explanations.keys() else "Incorrect input. Try again.", bounds[env][0] if env in bounds.keys() else None, - bounds[env][1] if env in bounds.keys() and len(bounds[env]) > 1 else None, - oob_errors[env] if env in oob_errors.keys() else "Input too long/short.", + bounds[env][1] if env in bounds.keys() and len( + bounds[env]) > 1 else None, + oob_errors[env] if env in oob_errors.keys( + ) else "Input too long/short.", extra_info="[#C0CAF5 bold]⮶ " + ( - explanations[env] if env in explanations.keys() else "No info available" + explanations[env] if env in explanations.keys( + ) else "No info available" ), ) ) diff --git a/utils/cleanup.py b/utils/cleanup.py index 858cfe9..558307d 100644 --- a/utils/cleanup.py +++ b/utils/cleanup.py @@ -7,10 +7,11 @@ def cleanup() -> int: Returns: int: How many files were deleted - """ + """ if exists("./assets/temp"): count = 0 - files = [f for f in os.listdir(".") if f.endswith(".mp4") and "temp" in f.lower()] + files = [f for f in os.listdir(".") if f.endswith( + ".mp4") and "temp" in f.lower()] count += len(files) for f in files: os.remove(f) diff --git a/utils/subreddit.py b/utils/subreddit.py index 3c5cb22..e350722 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -13,7 +13,7 @@ def get_subreddit_undone(submissions: list, subreddit): Returns: Any: The submission that has not been done - """ + """ """ recursively checks if the top submission in the list was already done. """ @@ -28,7 +28,8 @@ def get_subreddit_undone(submissions: list, subreddit): print_substep("NSFW Post Detected. Skipping...") continue except AttributeError: - print_substep("NSFW settings not defined. Skipping NSFW post...") + print_substep( + "NSFW settings not defined. Skipping NSFW post...") return submission print("all submissions have been done going by top submission order") return get_subreddit_undone( @@ -36,7 +37,7 @@ def get_subreddit_undone(submissions: list, subreddit): ) # all of the videos in hot have already been done -def already_done(done_videos: list, submission)->bool: +def already_done(done_videos: list, submission) -> bool: """Checks to see if the given submission is in the list of videos Args: @@ -45,7 +46,7 @@ def already_done(done_videos: list, submission)->bool: Returns: Boolean: Whether the video was found in the list - """ + """ for video in done_videos: if video["id"] == str(submission): diff --git a/utils/videos.py b/utils/videos.py index e6510fe..27f26bc 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -5,8 +5,8 @@ from utils.console import print_step def check_done( - redditobj:dict[str], -)->dict[str]|None: # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack + redditobj: dict[str], +) -> dict[str] | None: # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack """Checks if the chosen post has already been generated Args: @@ -25,6 +25,7 @@ def check_done( "You already have done this video but since it was declared specifically in the .env file the program will continue" ) return redditobj - print_step("Getting new post as the current one has already been done") + print_step( + "Getting new post as the current one has already been done") return None return redditobj diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 47663fb..667fe21 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -28,13 +28,13 @@ console = Console() W, H = 1080, 1920 -def make_final_video(number_of_clips:int, length:int,final_vid_path:str): +def make_final_video(number_of_clips: int, length: int, final_vid_path: str): """Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp Args: number_of_clips (int): Index to end at when going through the screenshots length (int): Length of the video - """ + """ print_step("Creating the final video 🎥") VideoFileClip.reW = lambda clip: clip.resize(width=W) VideoFileClip.reH = lambda clip: clip.resize(width=H) @@ -49,10 +49,10 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str): try: opacity = float(os.getenv("OPACITY")) except ( - ValueError, - FloatingPointError, - TypeError - ): + ValueError, + FloatingPointError, + TypeError + ): print_substep( "Please ensure that OPACITY is between 0 and 1 in .env file", style_="bold red" ) @@ -66,16 +66,15 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str): try: audio_clips.insert(1, AudioFileClip("assets/mp3/posttext.mp3")) except ( - OSError, - FileNotFoundError, - ): + OSError, + FileNotFoundError, + ): print_substep("An error occured! Aborting.", style_="bold red") raise SystemExit() else: audio_concat = concatenate_audioclips(audio_clips) audio_composite = CompositeAudioClip([audio_concat]) - # Get sum of all clip lengths total_length = sum([clip.duration for clip in audio_clips]) # round total_length to an integer @@ -142,13 +141,12 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str): final = CompositeVideoClip([background_clip, image_concat]) if final_vid_path is None: - final_vid_path = re.sub( + final_vid_path = re.sub( "[?\"%*:|<>]/", "", (f"assets/{subreddit.submission.title}.mp4") ) - final.write_videofile(final_vid_path, fps=30, audio_codec="aac", audio_bitrate="192k") - - + final.write_videofile(final_vid_path, fps=30, + audio_codec="aac", audio_bitrate="192k") save_data(final_vid_path) @@ -177,12 +175,13 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str): f"Reddit title: {os.getenv('VIDEO_TITLE')} \n Background Credit: {os.getenv('background_credit')}" ) -def save_data(filename:str): + +def save_data(filename: str): """Saves the videos that have already been generated to a JSON file in video_creation/data/videos.json Args: filename (str): The finished video title name - """ + """ with open("./video_creation/data/videos.json", "r+") as raw_vids: done_vids = json.load(raw_vids) if str(subreddit.submission.id) in [video["id"] for video in done_vids]: @@ -198,12 +197,13 @@ def save_data(filename:str): raw_vids.seek(0) json.dump(done_vids, raw_vids, ensure_ascii=False, indent=4) + def get_video_title() -> str: """Gets video title from env variable or gives it the name "final_video" Returns: str: Video title - """ + """ title = os.getenv("VIDEO_TITLE") or "final_video" if len(title) <= 35: return title diff --git a/video_creation/voices.py b/video_creation/voices.py index e88c98c..7dd077f 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -27,7 +27,7 @@ TTSProviders = { VIDEO_LENGTH: int = 40 # secs -def save_text_to_mp3(reddit_obj:dict[str])->tuple[int,int]: +def save_text_to_mp3(reddit_obj: dict[str]) -> tuple[int, int]: """Saves text to MP3 files. Goes through the reddit_obj and generates the title MP3 file and a certain number of comments until the total amount of time exceeds VIDEO_LENGTH seconds. Args: @@ -36,7 +36,7 @@ def save_text_to_mp3(reddit_obj:dict[str])->tuple[int,int]: Returns: tuple[int,int]: (total length of the audio, the number of comments audio was generated for) """ - + env = os.getenv("TTSCHOICE", "") if env.casefold() in map(lambda _: _.casefold(), TTSProviders): text_to_mp3 = TTSEngine(