From b8e9ce073a3d0ff061beb90934b750f5ffe73c04 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 4 Jul 2022 13:29:55 -0400 Subject: [PATCH 01/43] docs: changed default voice --- .env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 7cf7cc2..563cd07 100644 --- a/.env.template +++ b/.env.template @@ -68,7 +68,7 @@ OPACITY="1" #.8 POSTLANG="" #EXPLANATION Activates the translation feature, set the language code for translate or leave blank -TTSCHOICE="Polly" +TTSCHOICE="StreamlabsPolly" #EXPLANATION the backend used for TTS. Without anything specified, the user will be prompted to choose one. # IMPORTANT NOTE: if you use translate, you need to set this to googletranslate or tiktok and use custom voice in your language From eefd56e16f022b86c7bcb0eb1f5b4e5dfcd249b7 Mon Sep 17 00:00:00 2001 From: CordlessCoder <42666308+CordlessCoder@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:18:17 +0000 Subject: [PATCH 02/43] Add files via upload --- utils/console.py | 85 +++++++++----- utils/tomlchecker.py | 262 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 318 insertions(+), 29 deletions(-) create mode 100644 utils/tomlchecker.py diff --git a/utils/console.py b/utils/console.py index 5b91fef..8a68280 100644 --- a/utils/console.py +++ b/utils/console.py @@ -4,7 +4,6 @@ from rich.markdown import Markdown from rich.padding import Padding from rich.panel import Panel from rich.text import Text -from rich.columns import Columns import re console = Console() @@ -29,12 +28,6 @@ def print_substep(text, style=""): console.print(text, style=style) -def print_table(items): - """Prints items in a table.""" - - console.print(Columns([Panel(f"[yellow]{item}", expand=True) for item in items])) - - def handle_input( message: str = "", check_type=False, @@ -44,33 +37,67 @@ def handle_input( nmax=None, oob_error="", extra_info="", + options: list = None, + default=NotImplemented, ): - match = re.compile(match + "$") - console.print(extra_info, no_wrap=True) - while True: - console.print(message, end="") - user_input = input("").strip() - if re.match(match, user_input) is not None: + if default is not NotImplemented: + console.print( + "[green]" + + message + + '\n[blue bold]The default value is "' + + str(default) + + '"\nDo you want to use it?(y/n)' + ) + if input().casefold().startswith("y"): + return default + if options is None: + match = re.compile(match) + console.print("[green bold]" + extra_info, no_wrap=True) + while True: + console.print(message, end="") + user_input = input("").strip() if check_type is not False: try: - user_input = check_type(user_input) # this line is fine - if nmin is not None and user_input < nmin: - console.print("[red]" + oob_error) # Input too low failstate - continue - if nmax is not None and user_input > nmax: - console.print("[red]" + oob_error) # Input too high + user_input = check_type(user_input) + if (nmin is not None and user_input < nmin) or ( + nmax is not None and user_input > nmax + ): + # FAILSTATE Input out of bounds + console.print("[red]" + oob_error) continue break # Successful type conversion and number in bounds except ValueError: - console.print("[red]" + err_message) # Type conversion failed + # Type conversion failed + console.print("[red]" + err_message) continue - if nmin is not None and len(user_input) < nmin: # Check if string is long enough - console.print("[red]" + oob_error) + elif match != "" and re.match(match, user_input) is None: + console.print( + "[red]" + err_message + + "\nAre you absolutely sure it's correct?(y/n)" + ) + if input().casefold().startswith("y"): + break continue - if nmax is not None and len(user_input) > nmax: # Check if string is not too long - console.print("[red]" + oob_error) - continue - break - console.print("[red]" + err_message) - - return user_input + else: + # FAILSTATE Input STRING out of bounds + if (nmin is not None and len(user_input) < nmin) or ( + nmax is not None and len(user_input) > nmax + ): + console.print("[red bold]" + oob_error) + continue + break # SUCCESS Input STRING in bounds + return user_input + console.print(extra_info, no_wrap=True) + while True: + console.print(message, end="") + user_input = input("").strip() + if user_input not in options: + console.print( + "[red bold]" + + err_message + + "\nValid options are: " + + ", ".join(map(str, options)) + + "." + ) + continue + return user_input diff --git a/utils/tomlchecker.py b/utils/tomlchecker.py new file mode 100644 index 0000000..c44108f --- /dev/null +++ b/utils/tomlchecker.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# import os +import toml +from rich import pretty +from rich.console import Console +import re + +# from utils.console import handle_input +from console import handle_input + + +console = Console() + + +printed = False + + +def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): + for key in obj.keys(): + if type(obj[key]) is dict: + crawl(obj[key], func, path + [key]) + continue + func(path + [key], obj[key]) + + +def check(value, checks, name): + global printed + if printed is False: + console.print( + """\ +[blue bold]############################### +# # +# Checking TOML configuration # +# # +############################### +If you see any prompts, that means that you have unset/incorrectly set variables, please input the correct values.\ +""" + ) + printed = True + if "type" in checks: + try: + value = eval(checks["type"])(value) + except: + value = handle_input( + message=( + ( + ("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks + else "" + ) + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + " [#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + check_type=eval(checks["type"]), + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + + if ( + "options" in checks and value not in checks["options"] + ): # FAILSTATE Value is not one of the options + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + default=checks["default"] if "default" in checks else NotImplemented, + options=checks["options"], + ) + if "regex" in checks and ( + (isinstance(value, str) and re.match(checks["regex"], value) is None) + or not isinstance(value, str) + ): # FAILSTATE Value doesn't match regex, or has regex but is not a string. + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + match=checks["regex"], + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + default=checks["default"] if "default" in checks else NotImplemented, + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + + if not hasattr(value, "__iter__") and ( + ("nmin" in checks and checks["nmin"] + is not None and value < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) + ): + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + if hasattr(value, "__iter__") and ( + ("nmin" in checks and checks["nmin"] + is not None and len(value) < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) + ): + value = handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + if value == {}: + handle_input( + message=( + (("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks else "") + + "[red]" + + ("Non-optional ", "Optional ")[ + "optional" in checks and checks["optional"] is True + ] + ) + + "[#C0CAF5 bold]" + + str(name) + + "[#F7768E bold]=", + extra_info=checks["explanation"] if "explanation" in checks else "", + default=checks["default"] if "default" in checks else NotImplemented, + match=checks["regex"] if "regex" in checks else "", + err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + nmin=checks["nmin"] if "nmin" in checks else None, + nmax=checks["nmax"] if "nmax" in checks else None, + oob_error=checks["oob_error"] + if "oob_error" in checks + else "Input out of bounds(Value too high/low/long/short)", + ) + return value + + +def crawl_and_check(obj: dict, path: list, checks: dict = {}, name=""): + if len(path) == 0: + return check(obj, checks, name) + if path[0] not in obj.keys(): + obj[path[0]] = {} + obj[path[0]] = crawl_and_check(obj[path[0]], path[1:], checks, path[0]) + return obj + + +def check_vars(path, checks): + global config + crawl_and_check(config, path, checks) + + +def check_toml(template_file, config_file) -> bool: + try: + template = toml.load(template_file) + except Exception as error: + console.print( + f"[red bold]Encountered error when trying to to load {template_file}: {error}" + ) + return False + try: + global config + config = toml.load(config_file) + except (toml.TomlDecodeError): + console.print( + f"""[blue]Couldn't read {config_file}. +Overwrite it?(y/n)""" + ) + if not input().startswith("y"): + print("Unable to read config, and not allowed to overwrite it. Giving up.") + return False + else: + try: + with open(config_file, "w") as f: + f.write("") + except: + console.print( + f"[red bold]Failed to overwrite {config_file}. Giving up.\nSuggestion: check {config_file} permissions for the user." + ) + return False + except (FileNotFoundError): + console.print( + f"""[blue]Couldn't find {config_file} +Creating it now.""" + ) + try: + with open(config_file, "x") as f: + f.write("") + config = {} + except: + console.print( + f"[red bold]Failed to write to {config_file}. Giving up.\nSuggestion: check the folder's permissions for the user." + ) + return False + crawl(template, check_vars) + pretty.pprint(config) + with open(config_file, "w") as f: + toml.dump(config, f) + return True + + +if __name__ == "__main__": + check_toml(".config.template.toml", "config.toml") From e91209fd533192cedf009f64b9cc16183b819c82 Mon Sep 17 00:00:00 2001 From: CordlessCoder <42666308+CordlessCoder@users.noreply.github.com> Date: Tue, 5 Jul 2022 19:20:06 +0300 Subject: [PATCH 03/43] Update console.py --- utils/console.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/utils/console.py b/utils/console.py index 8a68280..fdab5d5 100644 --- a/utils/console.py +++ b/utils/console.py @@ -4,6 +4,7 @@ from rich.markdown import Markdown from rich.padding import Padding from rich.panel import Panel from rich.text import Text +from rich.columns import Columns import re console = Console() @@ -22,7 +23,14 @@ def print_step(text): panel = Panel(Text(text, justify="left")) console.print(panel) + +def print_table(items): + """Prints items in a table.""" + console.print(Columns([Panel(f"[yellow]{item}", expand=True) for item in items])) + + + def print_substep(text, style=""): """Prints a rich info message without the panelling.""" console.print(text, style=style) From c8817d9ffd1145678d35101badbe9f46a876d9cd Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 20:02:04 +0300 Subject: [PATCH 04/43] painstakingly restored .config.template.toml --- .config.template.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .config.template.toml diff --git a/.config.template.toml b/.config.template.toml new file mode 100644 index 0000000..e7de56f --- /dev/null +++ b/.config.template.toml @@ -0,0 +1,41 @@ +[reddit.creds] +client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The ID should be over 12 and under 30 characters, double check your input." } +client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } +username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "asdfghjkl", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } +password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } +2fa = { optional = true, options = [ + true, + false, +], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and Talse", example = true } + + +[reddit.thread] +random = { optional = true, options = [ + true, + false, +], default = false, 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" } +post_id = { optional = false, regex = "^((?!://|://).)*$", 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" } + + +[settings] +allow_nsfw = { optional = true, default = false, example = false, options = [ + true, + false, +], explanation = "Whether to allow NSFW content, True or False" } +theme = { optional = true, default = "light", example = "dark", options = [ + "dark", + "light", +], explanation = "sets the Reddit theme, either LIGHT or DARK" } +times_to_run = { optional = true, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } +opacity = { optional = true, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } +storymode = { optional = true, default = false, example = false, options = [ + true, + false, +] } + + +# [settings.tts] +# voice = { optional = true, example = "en_us_002", explanation = "sets the voice used by TTS" } +# choice = { optional = true, example = "polly", explanation = "the backend used " } From ad30e9a8d9ac35e77ffe6e9316a1d1f93340b2d1 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 20:03:52 +0300 Subject: [PATCH 05/43] removed a debug print statement --- config.toml | 19 +++++++++++++++++++ utils/tomlchecker.py | 23 ++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 config.toml mode change 100644 => 100755 utils/tomlchecker.py diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..baba72c --- /dev/null +++ b/config.toml @@ -0,0 +1,19 @@ +[settings] +allow_nsfw = false +theme = "light" +times_to_run = 1 +opacity = 0.9 +storymode = false + +[reddit.creds] +client_id = "adsfadsfasdf" +client_secret = "adsfasdfadsfasdfasdf" +username = "asdfasdfadsf" +password = "asdfasdfadsf" +2fa = false + +[reddit.thread] +random = false +subreddit = "asdfasdfadsfadsfadfs" +post_id = "asdfasdfasdf" +max_comment_length = 500 diff --git a/utils/tomlchecker.py b/utils/tomlchecker.py old mode 100644 new mode 100755 index c44108f..85f6a58 --- a/utils/tomlchecker.py +++ b/utils/tomlchecker.py @@ -73,8 +73,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables ): # FAILSTATE Value is not one of the options value = handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks else "") + (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -94,8 +93,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables ): # FAILSTATE Value doesn't match regex, or has regex but is not a string. value = handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks else "") + (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -116,14 +114,12 @@ If you see any prompts, that means that you have unset/incorrectly set variables ) if not hasattr(value, "__iter__") and ( - ("nmin" in checks and checks["nmin"] - is not None and value < checks["nmin"]) + ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"]) or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) ): value = handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks else "") + (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -143,14 +139,12 @@ If you see any prompts, that means that you have unset/incorrectly set variables else "Input out of bounds(Value too high/low/long/short)", ) if hasattr(value, "__iter__") and ( - ("nmin" in checks and checks["nmin"] - is not None and len(value) < checks["nmin"]) + ("nmin" in checks and checks["nmin"] is not None and len(value) < checks["nmin"]) or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) ): value = handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks else "") + (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -172,8 +166,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables if value == {}: handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks else "") + (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -252,7 +245,7 @@ Creating it now.""" ) return False crawl(template, check_vars) - pretty.pprint(config) + # pretty.pprint(config) with open(config_file, "w") as f: toml.dump(config, f) return True From 616da070080f51bc59d3c0f42d7b0192edeb9b2c Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 18:52:28 +0100 Subject: [PATCH 06/43] chore: remove config.toml and add to gitignore --- .gitignore | 2 ++ config.toml | 19 ------------------- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 config.toml diff --git a/.gitignore b/.gitignore index 4ee3693..793db5d 100644 --- a/.gitignore +++ b/.gitignore @@ -241,3 +241,5 @@ reddit-bot-351418-5560ebc49cac.json *.pyc video_creation/data/videos.json video_creation/data/envvars.txt + +config.toml diff --git a/config.toml b/config.toml deleted file mode 100644 index baba72c..0000000 --- a/config.toml +++ /dev/null @@ -1,19 +0,0 @@ -[settings] -allow_nsfw = false -theme = "light" -times_to_run = 1 -opacity = 0.9 -storymode = false - -[reddit.creds] -client_id = "adsfadsfasdf" -client_secret = "adsfasdfadsfasdfasdf" -username = "asdfasdfadsf" -password = "asdfasdfadsf" -2fa = false - -[reddit.thread] -random = false -subreddit = "asdfasdfadsfadsfadfs" -post_id = "asdfasdfasdf" -max_comment_length = 500 From 3c52d1b046c02e0284f50d3035d5e75a0a9f6f4d Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 22:39:22 +0300 Subject: [PATCH 07/43] chore: removed duplicate handle_input calls --- .config.template.toml | 8 +- config.toml | 19 ----- main.py | 23 +++--- utils/console.py | 38 +++++---- utils/tomlchecker.py | 183 +++++++++++------------------------------- 5 files changed, 88 insertions(+), 183 deletions(-) delete mode 100644 config.toml diff --git a/.config.template.toml b/.config.template.toml index e7de56f..1c5ee43 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -3,7 +3,7 @@ client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of y client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "asdfghjkl", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } -2fa = { optional = true, options = [ +2fa = { optional = true, type = "bool", options = [ true, false, ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and Talse", example = true } @@ -13,14 +13,14 @@ password = { optional = false, nmin = 8, explanation = "the password of your red random = { optional = true, options = [ true, false, -], default = false, 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" } +], 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" } post_id = { optional = false, regex = "^((?!://|://).)*$", 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" } [settings] -allow_nsfw = { optional = true, default = false, example = false, options = [ +allow_nsfw = { optional = true, type = "bool", default = false, example = false, options = [ true, false, ], explanation = "Whether to allow NSFW content, True or False" } @@ -30,7 +30,7 @@ theme = { optional = true, default = "light", example = "dark", options = [ ], explanation = "sets the Reddit theme, either LIGHT or DARK" } times_to_run = { optional = true, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } opacity = { optional = true, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } -storymode = { optional = true, default = false, example = false, options = [ +storymode = { optional = true, type = "bool", default = false, example = false, options = [ true, false, ] } diff --git a/config.toml b/config.toml deleted file mode 100644 index baba72c..0000000 --- a/config.toml +++ /dev/null @@ -1,19 +0,0 @@ -[settings] -allow_nsfw = false -theme = "light" -times_to_run = 1 -opacity = 0.9 -storymode = false - -[reddit.creds] -client_id = "adsfadsfasdf" -client_secret = "adsfasdfadsfasdfasdf" -username = "asdfasdfadsf" -password = "asdfasdfadsf" -2fa = false - -[reddit.thread] -random = false -subreddit = "asdfasdfadsfadsfadfs" -post_id = "asdfasdfasdf" -max_comment_length = 500 diff --git a/main.py b/main.py index e362aad..b7fdc5c 100755 --- a/main.py +++ b/main.py @@ -1,12 +1,11 @@ #!/usr/bin/env python import math from subprocess import Popen -from os import getenv, name -from dotenv import load_dotenv +from os import name from reddit.subreddit import get_subreddit_threads from utils.cleanup import cleanup from utils.console import print_markdown, print_step -from utils.checker import check_env +from utils.tomlchecker import check_toml # from utils.checker import envUpdate from video_creation.background import download_background, chop_background_video @@ -31,6 +30,7 @@ print_markdown( ) print_step(f"You are using V{VERSION} of the bot") + def main(POST_ID=None): cleanup() reddit_object = get_subreddit_threads(POST_ID) @@ -45,25 +45,24 @@ def main(POST_ID=None): def run_many(times): for x in range(1, times + 1): print_step( - f'on the {x}{("st" if x == 1 else ("nd" if x == 2 else ("rd" if x == 3 else "th")))} iteration of {times}' + f'on the {x}{("th", "st", "nd", "rd", "th", "th", "th", "th","th", "th")[x%10]} iteration of {times}' ) # correct 1st 2nd 3rd 4th 5th.... main() Popen("cls" if name == "nt" else "clear", shell=True).wait() if __name__ == "__main__": - if check_env() is not True: - exit() - load_dotenv() + config = check_toml(".config.template.toml", "config.toml") + config is False and exit() try: - if getenv("TIMES_TO_RUN") and isinstance(int(getenv("TIMES_TO_RUN")), int): - run_many(int(getenv("TIMES_TO_RUN"))) + if config["settings"]["times_to_run"]: + run_many(config["settings"]["times_to_run"]) - elif len(getenv("POST_ID", "").split("+")) > 1: - for index, post_id in enumerate(getenv("POST_ID", "").split("+")): + elif len(config["reddit"]["thread"]["post_id"].split("+")) > 1: + for index, post_id in enumerate(config["reddit"]["thread"]["post_id"].split("+")): index += 1 print_step( - f'on the {index}{("st" if index == 1 else ("nd" if index == 2 else ("rd" if index == 3 else "th")))} post of {len(getenv("POST_ID", "").split("+"))}' + f'on the {index}{("st" if index%10 == 1 else ("nd" if index%10 == 2 else ("rd" if index%10 == 3 else "th")))} post of {len(config["reddit"]["thread"]["post_id"].split("+"))}' ) main(post_id) Popen("cls" if name == "nt" else "clear", shell=True).wait() diff --git a/utils/console.py b/utils/console.py index fdab5d5..46396f2 100644 --- a/utils/console.py +++ b/utils/console.py @@ -23,14 +23,13 @@ def print_step(text): panel = Panel(Text(text, justify="left")) console.print(panel) - + def print_table(items): """Prints items in a table.""" console.print(Columns([Panel(f"[yellow]{item}", expand=True) for item in items])) - def print_substep(text, style=""): """Prints a rich info message without the panelling.""" console.print(text, style=style) @@ -80,8 +79,7 @@ def handle_input( continue elif match != "" and re.match(match, user_input) is None: console.print( - "[red]" + err_message + - "\nAre you absolutely sure it's correct?(y/n)" + "[red]" + err_message + "\nAre you absolutely sure it's correct?(y/n)" ) if input().casefold().startswith("y"): break @@ -99,13 +97,25 @@ def handle_input( while True: console.print(message, end="") user_input = input("").strip() - if user_input not in options: - console.print( - "[red bold]" - + err_message - + "\nValid options are: " - + ", ".join(map(str, options)) - + "." - ) - continue - return user_input + if check_type is not False: + try: + isinstance(eval(user_input), check_type) + return check_type(user_input) + except: + console.print( + "[red bold]" + + err_message + + "\nValid options are: " + + ", ".join(map(str, options)) + + "." + ) + continue + if user_input in options: + return user_input + console.print( + "[red bold]" + + err_message + + "\nValid options are: " + + ", ".join(map(str, options)) + + "." + ) diff --git a/utils/tomlchecker.py b/utils/tomlchecker.py index 85f6a58..c4e8c5a 100755 --- a/utils/tomlchecker.py +++ b/utils/tomlchecker.py @@ -5,14 +5,12 @@ from rich import pretty from rich.console import Console import re -# from utils.console import handle_input -from console import handle_input +from utils.console import handle_input - -console = Console() +# from console import handle_input -printed = False +console = Console() def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): @@ -24,124 +22,50 @@ def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): def check(value, checks, name): - global printed - if printed is False: - console.print( - """\ -[blue bold]############################### -# # -# Checking TOML configuration # -# # -############################### -If you see any prompts, that means that you have unset/incorrectly set variables, please input the correct values.\ -""" - ) - printed = True - if "type" in checks: + + incorrect = False + if value == {}: + incorrect = True + if not incorrect and "type" in checks: try: value = eval(checks["type"])(value) except: - value = handle_input( - message=( - ( - ("[blue]Example: " + str(checks["example"]) + "\n") - if "example" in checks - else "" - ) - + "[red]" - + ("Non-optional ", "Optional ")[ - "optional" in checks and checks["optional"] is True - ] - ) - + " [#C0CAF5 bold]" - + str(name) - + "[#F7768E bold]=", - check_type=eval(checks["type"]), - extra_info=checks["explanation"] if "explanation" in checks else "", - default=checks["default"] if "default" in checks else NotImplemented, - match=checks["regex"] if "regex" in checks else "", - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", - nmin=checks["nmin"] if "nmin" in checks else None, - nmax=checks["nmax"] if "nmax" in checks else None, - oob_error=checks["oob_error"] - if "oob_error" in checks - else "Input out of bounds(Value too high/low/long/short)", - ) + incorrect = True if ( - "options" in checks and value not in checks["options"] + not incorrect and "options" in checks and value not in checks["options"] ): # FAILSTATE Value is not one of the options - value = handle_input( - message=( - (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") - + "[red]" - + ("Non-optional ", "Optional ")[ - "optional" in checks and checks["optional"] is True - ] - ) - + "[#C0CAF5 bold]" - + str(name) - + "[#F7768E bold]=", - extra_info=checks["explanation"] if "explanation" in checks else "", - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", - default=checks["default"] if "default" in checks else NotImplemented, - options=checks["options"], + incorrect = True + if ( + not incorrect + and "regex" in checks + and ( + (isinstance(value, str) and re.match(checks["regex"], value) is None) + or not isinstance(value, str) ) - if "regex" in checks and ( - (isinstance(value, str) and re.match(checks["regex"], value) is None) - or not isinstance(value, str) ): # FAILSTATE Value doesn't match regex, or has regex but is not a string. - value = handle_input( - message=( - (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") - + "[red]" - + ("Non-optional ", "Optional ")[ - "optional" in checks and checks["optional"] is True - ] - ) - + "[#C0CAF5 bold]" - + str(name) - + "[#F7768E bold]=", - extra_info=checks["explanation"] if "explanation" in checks else "", - match=checks["regex"], - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", - default=checks["default"] if "default" in checks else NotImplemented, - nmin=checks["nmin"] if "nmin" in checks else None, - nmax=checks["nmax"] if "nmax" in checks else None, - oob_error=checks["oob_error"] - if "oob_error" in checks - else "Input out of bounds(Value too high/low/long/short)", - ) + incorrect = True - if not hasattr(value, "__iter__") and ( - ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"]) - or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) + if ( + not incorrect + and not hasattr(value, "__iter__") + and ( + ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) + ) ): - value = handle_input( - message=( - (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") - + "[red]" - + ("Non-optional ", "Optional ")[ - "optional" in checks and checks["optional"] is True - ] - ) - + "[#C0CAF5 bold]" - + str(name) - + "[#F7768E bold]=", - extra_info=checks["explanation"] if "explanation" in checks else "", - default=checks["default"] if "default" in checks else NotImplemented, - match=checks["regex"] if "regex" in checks else "", - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", - nmin=checks["nmin"] if "nmin" in checks else None, - nmax=checks["nmax"] if "nmax" in checks else None, - oob_error=checks["oob_error"] - if "oob_error" in checks - else "Input out of bounds(Value too high/low/long/short)", + incorrect = True + if ( + not incorrect + and hasattr(value, "__iter__") + and ( + ("nmin" in checks and checks["nmin"] is not None and len(value) < checks["nmin"]) + or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) ) - if hasattr(value, "__iter__") and ( - ("nmin" in checks and checks["nmin"] is not None and len(value) < checks["nmin"]) - or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) ): + incorrect = True + + if incorrect: value = handle_input( message=( (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") @@ -154,6 +78,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables + str(name) + "[#F7768E bold]=", extra_info=checks["explanation"] if "explanation" in checks else "", + check_type=eval(checks["type"]) if "type" in checks else False, default=checks["default"] if "default" in checks else NotImplemented, match=checks["regex"] if "regex" in checks else "", err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", @@ -162,28 +87,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables oob_error=checks["oob_error"] if "oob_error" in checks else "Input out of bounds(Value too high/low/long/short)", - ) - if value == {}: - handle_input( - message=( - (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") - + "[red]" - + ("Non-optional ", "Optional ")[ - "optional" in checks and checks["optional"] is True - ] - ) - + "[#C0CAF5 bold]" - + str(name) - + "[#F7768E bold]=", - extra_info=checks["explanation"] if "explanation" in checks else "", - default=checks["default"] if "default" in checks else NotImplemented, - match=checks["regex"] if "regex" in checks else "", - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", - nmin=checks["nmin"] if "nmin" in checks else None, - nmax=checks["nmax"] if "nmax" in checks else None, - oob_error=checks["oob_error"] - if "oob_error" in checks - else "Input out of bounds(Value too high/low/long/short)", + options=checks["options"] if "options" in checks else None, ) return value @@ -244,11 +148,22 @@ Creating it now.""" f"[red bold]Failed to write to {config_file}. Giving up.\nSuggestion: check the folder's permissions for the user." ) return False + + console.print( + """\ +[blue bold]############################### +# # +# Checking TOML configuration # +# # +############################### +If you see any prompts, that means that you have unset/incorrectly set variables, please input the correct values.\ +""" + ) crawl(template, check_vars) # pretty.pprint(config) with open(config_file, "w") as f: toml.dump(config, f) - return True + return config if __name__ == "__main__": From 4d9ce59f24d516a8414b13d1ec4af351fd4d3da9 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 23:25:15 +0300 Subject: [PATCH 08/43] feat: correct optional variable behavour --- .config.template.toml | 10 ++-- .env.template | 86 --------------------------- main.py | 2 +- utils/console.py | 5 ++ utils/{tomlchecker.py => settings.py} | 16 +++-- 5 files changed, 23 insertions(+), 96 deletions(-) delete mode 100644 .env.template rename utils/{tomlchecker.py => settings.py} (91%) diff --git a/.config.template.toml b/.config.template.toml index 1c5ee43..d12d30b 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -15,21 +15,21 @@ random = { optional = true, options = [ 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" } -post_id = { optional = false, regex = "^((?!://|://).)*$", explanation = "Used if you want to use a specific post.", example = "urdtfx" } +post_id = { optional = true, regex = "^((?!://|://).)*$", 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" } [settings] -allow_nsfw = { optional = true, type = "bool", default = false, example = false, options = [ +allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ true, false, ], explanation = "Whether to allow NSFW content, True or False" } -theme = { optional = true, default = "light", example = "dark", options = [ +theme = { optional = false, default = "light", example = "dark", options = [ "dark", "light", ], explanation = "sets the Reddit theme, either LIGHT or DARK" } -times_to_run = { optional = true, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } -opacity = { optional = true, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } +times_to_run = { optional = false, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } +opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } storymode = { optional = true, type = "bool", default = false, example = false, options = [ true, false, diff --git a/.env.template b/.env.template deleted file mode 100644 index 77f2acf..0000000 --- a/.env.template +++ /dev/null @@ -1,86 +0,0 @@ - -REDDIT_CLIENT_ID="" #fFAGRNJru1FTz70BzhT3Zg -#EXPLANATION the ID of your Reddit app of SCRIPT type -#RANGE 12:30 -#MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ -#OOB_ERROR The ID should be over 12 and under 30 characters, double check your input. - -REDDIT_CLIENT_SECRET="" #fFAGRNJru1FTz70BzhT3Zg -#EXPLANATION the SECRET of your Reddit app of SCRIPT type -#RANGE 20:40 -#MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ -#OOB_ERROR The secret should be over 20 and under 40 characters, double check your input. - -REDDIT_USERNAME="" #asdfghjkl -#EXPLANATION the username of your reddit account -#RANGE 3:20 -#MATCH_REGEX [-_0-9a-zA-Z]+$ -#OOB_ERROR A username HAS to be between 3 and 20 characters - -REDDIT_PASSWORD="" #fFAGRNJru1FTz70BzhT3Zg -#EXPLANATION the password of your reddit account -#RANGE 8:None -#OOB_ERROR Password too short - -#OPTIONAL -RANDOM_THREAD="no" -# If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" - -REDDIT_2FA="" #no -#MATCH_REGEX ^(yes|no) -#EXPLANATION Whether you have Reddit 2FA enabled, Valid options are "yes" and "no" - -SUBREDDIT="AskReddit" -#EXPLANATION what subreddit to pull posts from, the name of the sub, not the URL -#RANGE 3:20 -#MATCH_REGEX [_0-9a-zA-Z]+$ -#OOB_ERROR A subreddit name HAS to be between 3 and 20 characters - -ALLOW_NSFW="False" -#EXPLANATION Whether to allow NSFW content, True or False -#MATCH_REGEX ^(True|False)$ - -POST_ID="" -#MATCH_REGEX ^((?!://|://).)*$ -#EXPLANATION Used if you want to use a specific post. example of one is urdtfx - -THEME="LIGHT" #dark -#EXPLANATION sets the Reddit theme, either LIGHT or DARK -#MATCH_REGEX ^(dark|light|DARK|LIGHT)$ - -TIMES_TO_RUN="" #2 -#EXPLANATION used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1 - -MAX_COMMENT_LENGTH="500" #500 -#EXPLANATION max number of characters a comment can have. default is 500 -#RANGE 0:10000 -#MATCH_TYPE int -#OOB_ERROR the max comment length should be between 0 and 10000 - -OPACITY="1" #.8 -#EXPLANATION Sets the opacity of the comments when overlayed over the background -#RANGE 0:1 -#MATCH_TYPE float -#OOB_ERROR The opacity HAS to be between 0 and 1 - -# If you want to translate the comments to another language, set the language code here. -# If empty, no translation will be done. -POSTLANG="" -#EXPLANATION Activates the translation feature, set the language code for translate or leave blank - -TTSCHOICE="Polly" -#EXPLANATION the backend used for TTS. Without anything specified, the user will be prompted to choose one. -# IMPORTANT NOTE: if you use translate, you need to set this to googletranslate or tiktok and use custom voice in your language - -STREAMLABS_VOICE="Joanna" -#EXPLANATION Sets the voice for the Streamlabs Polly TTS Engine. Check the file for more information on different voices. - -AWS_VOICE="Joanna" -#EXPLANATION Sets the voice for the AWS Polly TTS Engine. Check the file for more information on different voices. - -TIKTOK_VOICE="en_us_006" -#EXPLANATION Sets the voice for the TikTok TTS Engine. Check the file for more information on different voices. - -#OPTIONAL -STORYMODE="False" -# IN-PROGRESS - not yet implemented diff --git a/main.py b/main.py index b7fdc5c..8fc3b59 100755 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ from os import name from reddit.subreddit import get_subreddit_threads from utils.cleanup import cleanup from utils.console import print_markdown, print_step -from utils.tomlchecker import check_toml +from utils.settings import check_toml # from utils.checker import envUpdate from video_creation.background import download_background, chop_background_video diff --git a/utils/console.py b/utils/console.py index 46396f2..310247d 100644 --- a/utils/console.py +++ b/utils/console.py @@ -46,7 +46,12 @@ def handle_input( extra_info="", options: list = None, default=NotImplemented, + optional=False, ): + if optional: + console.print(message + "\n[green]This is an optional value. Do you want to skip it? (y/n)") + if input().casefold().startswith("y"): + return None if default is not NotImplemented: console.print( "[green]" diff --git a/utils/tomlchecker.py b/utils/settings.py similarity index 91% rename from utils/tomlchecker.py rename to utils/settings.py index c4e8c5a..927c500 100755 --- a/utils/tomlchecker.py +++ b/utils/settings.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # import os import toml -from rich import pretty from rich.console import Console import re @@ -25,6 +24,8 @@ def check(value, checks, name): incorrect = False if value == {}: + if skip_opt and "optional" in checks and checks["optional"] is True: + return None incorrect = True if not incorrect and "type" in checks: try: @@ -88,6 +89,7 @@ def check(value, checks, name): if "oob_error" in checks else "Input out of bounds(Value too high/low/long/short)", options=checks["options"] if "options" in checks else None, + optional=checks["optional"] if "optional" in checks else False, ) return value @@ -103,10 +105,13 @@ def crawl_and_check(obj: dict, path: list, checks: dict = {}, name=""): def check_vars(path, checks): global config + global skip_opt + skip_opt = "skip_opt" in config crawl_and_check(config, path, checks) -def check_toml(template_file, config_file) -> bool: +def check_toml(template_file, config_file) -> (bool, dict): + global config try: template = toml.load(template_file) except Exception as error: @@ -115,7 +120,6 @@ def check_toml(template_file, config_file) -> bool: ) return False try: - global config config = toml.load(config_file) except (toml.TomlDecodeError): console.print( @@ -160,7 +164,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables """ ) crawl(template, check_vars) - # pretty.pprint(config) + config["skip_opt"] = True with open(config_file, "w") as f: toml.dump(config, f) return config @@ -168,3 +172,7 @@ If you see any prompts, that means that you have unset/incorrectly set variables if __name__ == "__main__": check_toml(".config.template.toml", "config.toml") + +if __name__ == "__main__": + check_toml(".config.template.toml", "config.toml") + check_toml(".config.template.toml", "config.toml") From 4c1c80ffd7e2be02b74443cb04cb3817b9ee28e4 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 23:26:07 +0300 Subject: [PATCH 09/43] chore: removed unused files --- utils/checker.py | 193 ----------------------------------------------- utils/config.py | 46 ----------- 2 files changed, 239 deletions(-) delete mode 100755 utils/checker.py delete mode 100644 utils/config.py diff --git a/utils/checker.py b/utils/checker.py deleted file mode 100755 index 791a376..0000000 --- a/utils/checker.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python -import os -from rich.console import Console -from rich.table import Table -from rich import box -import re -import dotenv -from utils.console import handle_input - -console = Console() - - -def check_env() -> bool: - """Checks to see what's been put in .env - - 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.") - return True - if not os.path.exists(".env"): - console.print("[red]Couldn't find the .env file, creating one now.") - with open(".env", "x", encoding="utf-8") as file: - file.write("") - success = True - with open(".env.template", "r", encoding="utf-8") as template: - # req_envs = [env.split("=")[0] for env in template.readlines() if "=" in env] - matching = {} - explanations = {} - bounds = {} - types = {} - oob_errors = {} - examples = {} - req_envs = [] - var_optional = False - for line in template.readlines(): - 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() - elif "#OPTIONAL" in line: - var_optional = True - elif line.startswith("#MATCH_REGEX "): - 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] - var_optional = False - elif line.startswith("#RANGE "): - bounds[req_envs[-1]] = tuple( - map( - lambda x: float(x) if x != "None" else None, - line.removeprefix("#RANGE ")[:-1].split(":"), - ) - ) - var_optional = False - elif line.startswith("#MATCH_TYPE "): - 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] - var_optional = False - else: - var_optional = False - missing = set() - incorrect = set() - dotenv.load_dotenv() - for env in req_envs: - value = os.getenv(env) - if value is None: - missing.add(env) - continue - if env in matching.keys(): - re.match(matching[env], value) is None and incorrect.add(env) - if env in bounds.keys() and env not in types.keys(): - len(value) >= bounds[env][0] or ( - len(bounds[env]) > 1 and bounds[env][1] >= len(value) - ) or incorrect.add(env) - continue - if env in types.keys(): - try: - temp = types[env](value) - if env in bounds.keys(): - (bounds[env][0] <= temp or incorrect.add(env)) and len(bounds[env]) > 1 and ( - bounds[env][1] >= temp or incorrect.add(env) - ) - except ValueError: - incorrect.add(env) - - if len(missing): - table = Table( - title="Missing variables", - highlight=True, - show_lines=True, - box=box.ROUNDED, - border_style="#414868", - header_style="#C0CAF5 bold", - 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("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", - 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][1]) - if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None - else "", - ) - console.print(table) - success = False - if len(incorrect): - table = Table( - title="Incorrect variables", - highlight=True, - show_lines=True, - box=box.ROUNDED, - border_style="#414868", - header_style="#C0CAF5 bold", - 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("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", - 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", - ) - missing.add(env) - console.print(table) - success = False - if success is True: - return True - console.print( - "[green]Do you want to automatically overwrite incorrect variables and add the missing variables? (y/n)" - ) - if not input().casefold().startswith("y"): - console.print("[red]Aborting: Unresolved missing variables") - return False - if len(incorrect): - with open(".env", "r+", encoding="utf-8") as env_file: - lines = [] - for line in env_file.readlines(): - 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") - with open(".env", "a", encoding="utf-8") as env_file: - for env in missing: - env_file.write( - env - + "=" - + ('"' if env not in types.keys() else "") - + str( - handle_input( - "[#F7768E bold]" + env + "[#C0CAF5 bold]=", - types[env] if env in types.keys() else False, - matching[env] if env in matching.keys() else ".*", - explanations[env] - 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.", - extra_info="[#C0CAF5 bold]⮶ " - + (explanations[env] if env in explanations.keys() else "No info available"), - ) - ) - + ('"' if env not in types.keys() else "") - + "\n" - ) - return True - - -if __name__ == "__main__": - check_env() diff --git a/utils/config.py b/utils/config.py deleted file mode 100644 index 29cbb79..0000000 --- a/utils/config.py +++ /dev/null @@ -1,46 +0,0 @@ -# write a class that takes .env file and parses it into a dictionary -from dotenv import dotenv_values - -DEFAULTS = { - "SUBREDDIT": "AskReddit", - "ALLOW_NSFW": "False", - "POST_ID": "", - "THEME": "DARK", - "REDDIT_2FA": "no", - "TIMES_TO_RUN": "", - "MAX_COMMENT_LENGTH": "500", - "OPACITY": "1", - "VOICE": "en_us_001", - "STORYMODE": "False", -} - - -class Config: - def __init__(self): - self.raw = dotenv_values("../.env") - self.load_attrs() - - def __getattr__(self, attr): # code completion for attributes fix. - return getattr(self, attr) - - def load_attrs(self): - for key, value in self.raw.items(): - self.add_attr(key, value) - - def add_attr(self, key, value): - if value is None or value == "": - setattr(self, key, DEFAULTS[key]) - else: - setattr(self, key, str(value)) - - -config = Config() - -print(config.SUBREDDIT) -# def temp(): -# root = '' -# if isinstance(root, praw.models.Submission): -# root_type = 'submission' -# elif isinstance(root, praw.models.Comment): -# root_type = 'comment' -# From 75c731ce5cc05e56d3edf3b28b1f64ad51bfc726 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:01:39 +0100 Subject: [PATCH 10/43] fix: spelling error --- .config.template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config.template.toml b/.config.template.toml index d12d30b..0ea6da2 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -6,7 +6,7 @@ password = { optional = false, nmin = 8, explanation = "the password of your red 2fa = { optional = true, type = "bool", options = [ true, false, -], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and Talse", example = true } +], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true } [reddit.thread] From d2d593371bcde615469ed68c142eebd5afab6935 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:02:31 +0100 Subject: [PATCH 11/43] refactor: rename tomlchecker and move to global settings variable --- utils/settings.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/utils/settings.py b/utils/settings.py index 927c500..d674974 100755 --- a/utils/settings.py +++ b/utils/settings.py @@ -4,6 +4,8 @@ import toml from rich.console import Console import re +from typing import Tuple, Dict + from utils.console import handle_input # from console import handle_input @@ -110,8 +112,9 @@ def check_vars(path, checks): crawl_and_check(config, path, checks) -def check_toml(template_file, config_file) -> (bool, dict): +def check_toml(template_file, config_file) -> Tuple[bool, Dict]: global config + config = None try: template = toml.load(template_file) except Exception as error: @@ -172,7 +175,3 @@ If you see any prompts, that means that you have unset/incorrectly set variables if __name__ == "__main__": check_toml(".config.template.toml", "config.toml") - -if __name__ == "__main__": - check_toml(".config.template.toml", "config.toml") - check_toml(".config.template.toml", "config.toml") From a110f49e4494172b0f4d0a35a978dccdb94c8063 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:04:01 +0100 Subject: [PATCH 12/43] refactor: use new settings option --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 8fc3b59..7b7c726 100755 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ from os import name from reddit.subreddit import get_subreddit_threads from utils.cleanup import cleanup from utils.console import print_markdown, print_step -from utils.settings import check_toml +from utils import settings # from utils.checker import envUpdate from video_creation.background import download_background, chop_background_video @@ -52,7 +52,7 @@ def run_many(times): if __name__ == "__main__": - config = check_toml(".config.template.toml", "config.toml") + config = settings.check_toml(".config.template.toml", "config.toml") config is False and exit() try: if config["settings"]["times_to_run"]: From b1d27a3b848038161616e4e58d41debc1702704b Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:13:59 +0100 Subject: [PATCH 13/43] refactor: subreddit.py uses toml config --- utils/subreddit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/subreddit.py b/utils/subreddit.py index f6ca686..a5d7aa6 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -1,5 +1,5 @@ import json -from os import getenv +from utils import settings from utils.console import print_substep @@ -22,7 +22,7 @@ def get_subreddit_undone(submissions: list, subreddit): continue if submission.over_18: try: - if getenv("ALLOW_NSFW").casefold() == "false": + if settings.config["settings"]["allow_nsfw"] == "false": print_substep("NSFW Post Detected. Skipping...") continue except AttributeError: From bc631c57afde288540ddd0fd29671e2ae7eb836d Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:33:18 +0100 Subject: [PATCH 14/43] refactor: reddit/subreddit.py uses toml config --- reddit/subreddit.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/reddit/subreddit.py b/reddit/subreddit.py index e1f8940..5eed982 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -1,6 +1,6 @@ import re -from os import getenv +from utils import settings import praw from praw.models import MoreComments @@ -17,20 +17,20 @@ def get_subreddit_threads(POST_ID: str): print_substep("Logging into Reddit.") content = {} - if str(getenv("REDDIT_2FA")).casefold() == "yes": + if settings.config["reddit"]["creds"]["2fa"] == "true": print("\nEnter your two-factor authentication code from your authenticator app.\n") code = input("> ") print() - pw = getenv("REDDIT_PASSWORD") + pw = settings.config["reddit"]["creds"]["password"] passkey = f"{pw}:{code}" else: - passkey = getenv("REDDIT_PASSWORD") - username = getenv("REDDIT_USERNAME") + passkey = settings.config["reddit"]["creds"]["password"] + username = settings.config["reddit"]["creds"]["username"] if username.casefold().startswith("u/"): username = username[2:] reddit = praw.Reddit( - client_id=getenv("REDDIT_CLIENT_ID"), - client_secret=getenv("REDDIT_CLIENT_SECRET"), + client_id= settings.config["reddit"]["creds"]["client_id"], + client_secret=settings.config["reddit"]["creds"]["client_secret"], user_agent="Accessing Reddit threads", username=username, passkey=passkey, @@ -39,9 +39,7 @@ def get_subreddit_threads(POST_ID: str): # Ask user for subreddit input print_step("Getting subreddit threads...") - if not getenv( - "SUBREDDIT" - ): # note to user. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython") + if not settings.config["reddit"]["thread"]["subreddit"]: # note to user. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython") try: subreddit = reddit.subreddit( re.sub(r"r\/", "", input("What subreddit would you like to pull from? ")) @@ -51,8 +49,9 @@ def get_subreddit_threads(POST_ID: str): subreddit = reddit.subreddit("askreddit") print_substep("Subreddit not defined. Using AskReddit.") else: - print_substep(f"Using subreddit: r/{getenv('SUBREDDIT')} from environment variable config") - subreddit_choice = getenv("SUBREDDIT") + 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 subreddit_choice = subreddit_choice[2:] subreddit = reddit.subreddit( @@ -61,8 +60,8 @@ def get_subreddit_threads(POST_ID: str): if POST_ID: # would only be called if there are multiple queued posts submission = reddit.submission(id=POST_ID) - elif getenv("POST_ID") and len(getenv("POST_ID").split("+")) == 1: - submission = reddit.submission(id=getenv("POST_ID")) + elif settings.config["reddit"]["thread"]["post_id"] and len(settings.config["reddit"]["thread"]["post_id"].split("+")) == 1: + submission = reddit.submission(id=settings.config["reddit"]["thread"]["post_id"]) else: threads = subreddit.hot(limit=25) @@ -91,7 +90,7 @@ def get_subreddit_threads(POST_ID: str): if top_level_comment.body in ["[removed]", "[deleted]"]: continue # # see https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/78 if not top_level_comment.stickied: - if len(top_level_comment.body) <= int(getenv("MAX_COMMENT_LENGTH", "500")): + if len(top_level_comment.body) <= int(settings.config["reddit"]["thread"]["max_comment_length"]): if ( top_level_comment.author is not None ): # if errors occur with this change to if not. From 8bc72da9c4e2771e617c3dda1005247ecec927ac Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:34:09 +0100 Subject: [PATCH 15/43] refactor: utils/videos.py uses new toml config --- utils/videos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/videos.py b/utils/videos.py index 07659f6..4e248b9 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -1,11 +1,11 @@ import json import os import time -from os import getenv from typing import Dict from praw.models import Submission +from utils import settings from utils.console import print_step @@ -25,7 +25,7 @@ def check_done( done_videos = json.load(done_vids_raw) for video in done_videos: if video["id"] == str(redditobj): - if getenv("POST_ID"): + if settings.config["reddit"]["thread"]["post_id"]: print_step( "You already have done this video but since it was declared specifically in the .env file the program will continue" ) From 49eec71f0597fab38a53296558bb1be268815bb8 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 5 Jul 2022 23:51:49 +0300 Subject: [PATCH 16/43] refactor: utils/videos.py uses new toml config(fix) --- utils/videos.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/videos.py b/utils/videos.py index 4e248b9..6377256 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -1,5 +1,4 @@ import json -import os import time from typing import Dict @@ -51,7 +50,9 @@ def save_data(filename: str, reddit_title: str, reddit_id: str): payload = { "id": reddit_id, "time": str(int(time.time())), - "background_credit": str(os.getenv("background_credit")), + "background_credit": settings.config["settings"]["background_credit"] + if "background_credit" in settings.config["settings"] + else "", "reddit_title": reddit_title, "filename": filename, } From c1a4b51f410437c7c8180a03a48a9112f314a4a0 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:56:04 +0100 Subject: [PATCH 17/43] feat: prepare config template for TTS --- .config.template.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.config.template.toml b/.config.template.toml index 0ea6da2..5401118 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -36,6 +36,8 @@ storymode = { optional = true, type = "bool", default = false, example = false, ] } -# [settings.tts] -# voice = { optional = true, example = "en_us_002", explanation = "sets the voice used by TTS" } -# choice = { optional = true, example = "polly", explanation = "the backend used " } +[settings.tts] +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" } +choice = { optional = true, example = "streamlabspolly", explanation = "The backend used for TTS generation" } From 19ef71fc9afc78cffbcf515abe2fb45ba2baf894 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 21:56:31 +0100 Subject: [PATCH 18/43] refactor: voices.py uses toml config --- video_creation/voices.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/video_creation/voices.py b/video_creation/voices.py index 5105a10..ace67a4 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import os from typing import Dict, Tuple from rich.console import Console @@ -10,7 +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 utils import settings from utils.console import print_table, print_step @@ -34,9 +33,9 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]: 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(get_case_insensitive_key_value(TTSProviders, env), reddit_obj) + voice = settings.config["tts"]["choice"] + if voice.casefold() in map(lambda _: _.casefold(), TTSProviders): + text_to_mp3 = TTSEngine(get_case_insensitive_key_value(TTSProviders, voice), reddit_obj) else: while True: print_step("Please choose one of the following TTS providers: ") From ac04b665de2c59eac8be3e8a4d571ad8455d8b66 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 22:26:36 +0100 Subject: [PATCH 19/43] refactor: screenshot_downloader.py uses toml config --- video_creation/screenshot_downloader.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index e8afc44..c6ed035 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -1,9 +1,9 @@ import json import os -from os import getenv + from pathlib import Path from typing import Dict - +from utils import settings from playwright.async_api import async_playwright # pylint: disable=unused-import # do not remove the above line @@ -35,7 +35,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in browser = p.chromium.launch() context = browser.new_context() - if getenv("THEME").upper() == "DARK": + if settings.config["settings"]["theme"] == "dark": cookie_file = open("./video_creation/data/cookie-dark-mode.json", encoding="utf-8") else: cookie_file = open("./video_creation/data/cookie-light-mode.json", encoding="utf-8") @@ -56,7 +56,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in # translate code - if getenv("POSTLANG"): + if settings.config["reddit"]["thread"]["post_lang"]: print_substep("Translating post...") texts_in_tl = ts.google(reddit_object["thread_title"], to_language=os.getenv("POSTLANG")) @@ -88,9 +88,9 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in # translate code - if getenv("POSTLANG"): + if settings.config["reddit"]["thread"]["post_lang"]: comment_tl = ts.google( - comment["comment_body"], to_language=os.getenv("POSTLANG") + comment["comment_body"], to_language=settings.config["reddit"]["thread"]["post_lang"] ) page.evaluate( '([tl_content, tl_id]) => document.querySelector(`#t1_${tl_id} > div:nth-child(2) > div > div[data-testid="comment"] > div`).textContent = tl_content', From 5a2112b34e93d5a7e9f4ffb79faae1e757df1e9a Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 22:38:01 +0100 Subject: [PATCH 20/43] fix: incorrect settings path for tts --- video_creation/voices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video_creation/voices.py b/video_creation/voices.py index ace67a4..4224d15 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -33,7 +33,7 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]: tuple[int,int]: (total length of the audio, the number of comments audio was generated for) """ - voice = settings.config["tts"]["choice"] + voice = settings.config["settings"]["tts"]["choice"] if voice.casefold() in map(lambda _: _.casefold(), TTSProviders): text_to_mp3 = TTSEngine(get_case_insensitive_key_value(TTSProviders, voice), reddit_obj) else: From 9244cc9b2bc67b8dae6c7290077f961953455746 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 22:38:55 +0100 Subject: [PATCH 21/43] feat: add translation setting to template toml --- .config.template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config.template.toml b/.config.template.toml index 5401118..7f4a277 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -17,7 +17,7 @@ random = { optional = true, options = [ 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" } post_id = { optional = true, regex = "^((?!://|://).)*$", 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 = { optional = true, explanation = "The language you would like to translate to - leave blank for none.", example = "es-cr"} [settings] allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ From 0d212868d9af73ae4b49ebe643d09d85a3aaf1bc Mon Sep 17 00:00:00 2001 From: Jovan Date: Tue, 5 Jul 2022 23:53:26 +0200 Subject: [PATCH 22/43] Added translation for filename --- video_creation/final_video.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index d706361..be42d86 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -4,6 +4,7 @@ import os import re from os.path import exists from typing import Dict +import translators as ts from moviepy.editor import ( VideoFileClip, @@ -35,7 +36,14 @@ def name_normalize( name = re.sub(r'([0-9]+)\s?\/\s?([0-9]+)', r'\1 of \2', name) name = re.sub(r'(\w+)\s?\/\s?(\w+)', r'\1 or \2', name) name = re.sub(r'\/', r'', name) - return name + + if os.getenv("POSTLANG") != "": + print_substep("Translating filename...") + translated_name = ts.google(name, to_language=os.getenv("POSTLANG")) + return translated_name + + else: + return name def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): From 3e1b95e708aaed434d10b3aa149b2b6f2edd37d1 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Tue, 5 Jul 2022 23:22:35 +0100 Subject: [PATCH 23/43] fix: wrongly typed boolean --- reddit/subreddit.py | 2 +- utils/subreddit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 5eed982..40c82a1 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -17,7 +17,7 @@ def get_subreddit_threads(POST_ID: str): print_substep("Logging into Reddit.") content = {} - if settings.config["reddit"]["creds"]["2fa"] == "true": + if settings.config["reddit"]["creds"]["2fa"] == True: print("\nEnter your two-factor authentication code from your authenticator app.\n") code = input("> ") print() diff --git a/utils/subreddit.py b/utils/subreddit.py index a5d7aa6..140f4d3 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -22,7 +22,7 @@ def get_subreddit_undone(submissions: list, subreddit): continue if submission.over_18: try: - if settings.config["settings"]["allow_nsfw"] == "false": + if settings.config["settings"]["allow_nsfw"] == False: print_substep("NSFW Post Detected. Skipping...") continue except AttributeError: From 436266033ec0614addbedc74486179f38de7eade Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:23:27 +0100 Subject: [PATCH 24/43] refactor: tts engines now use toml config --- TTS/GTTS.py | 4 ++-- TTS/TikTok.py | 4 ++-- TTS/aws_polly.py | 6 +++--- TTS/engine_wrapper.py | 6 +++--- TTS/streamlabs_polly.py | 8 +++----- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/TTS/GTTS.py b/TTS/GTTS.py index 992eeb5..27b6934 100644 --- a/TTS/GTTS.py +++ b/TTS/GTTS.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import random -import os +from utils import settings from gtts import gTTS max_chars = 0 @@ -12,7 +12,7 @@ class GTTS: self.voices = [] def run(self, text, filepath): - tts = gTTS(text=text, lang=os.getenv("POSTLANG") or "en", slow=False) + tts = gTTS(text=text, lang=settings.config["reddit"]["thread"]["post_lang"] or "en", slow=False) tts.save(filepath) def randomvoice(self): diff --git a/TTS/TikTok.py b/TTS/TikTok.py index 91ba526..4583358 100644 --- a/TTS/TikTok.py +++ b/TTS/TikTok.py @@ -1,5 +1,5 @@ import base64 -import os +from utils import settings import random import requests from requests.adapters import HTTPAdapter, Retry @@ -75,7 +75,7 @@ class TikTok: # TikTok Text-to-Speech Wrapper voice = ( self.randomvoice() if random_voice - else (os.getenv("TIKTOK_VOICE") or random.choice(self.voices["human"])) + else (settings.config["settings"]["tts"]["tiktok_voice"] or random.choice(self.voices["human"])) ) try: r = requests.post(f"{self.URI_BASE}{voice}&req_text={text}&speaker_map_type=0") diff --git a/TTS/aws_polly.py b/TTS/aws_polly.py index 6cbe4c5..94143db 100644 --- a/TTS/aws_polly.py +++ b/TTS/aws_polly.py @@ -2,7 +2,7 @@ from boto3 import Session from botocore.exceptions import BotoCoreError, ClientError import sys -import os +from utils import settings import random voices = [ @@ -35,11 +35,11 @@ class AWSPolly: if random_voice: voice = self.randomvoice() else: - if not os.getenv("AWS_VOICE"): + if not settings.config["settings"]["tts"]["aws_polly_voice"]: return ValueError( f"Please set the environment variable AWS_VOICE to a valid voice. options are: {voices}" ) - voice = str(os.getenv("AWS_VOICE")).capitalize() + voice = str(settings.config["settings"]["tts"]["aws_polly_voice"]).capitalize() try: # Request speech synthesis response = polly.synthesize_speech( diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index 13ff850..c520ab0 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -2,7 +2,6 @@ from pathlib import Path from typing import Tuple import re -from os import getenv # import sox # from mutagen import MutagenError @@ -12,6 +11,7 @@ from rich.progress import track from moviepy.editor import AudioFileClip, CompositeAudioClip, concatenate_audioclips from utils.console import print_step, print_substep from utils.voice import sanitize_text +from utils import settings DEFUALT_MAX_LENGTH: int = 50 # video length variable @@ -56,7 +56,7 @@ class TTSEngine: print_step("Saving Text to MP3 files...") self.call_tts("title", self.reddit_object["thread_title"]) - if self.reddit_object["thread_post"] != "" and getenv("STORYMODE", "").casefold() == "true": + if self.reddit_object["thread_post"] != "" and settings.config["settings"]["storymode"] == True: self.call_tts("posttext", self.reddit_object["thread_post"]) idx = None @@ -109,7 +109,7 @@ class TTSEngine: clip.close() def process_text(text: str): - lang = getenv("POSTLANG", "") + lang = settings.config["reddit"]["thread"]["post_lang"] new_text = sanitize_text(text) if lang: print_substep("Translating Text...") diff --git a/TTS/streamlabs_polly.py b/TTS/streamlabs_polly.py index 066fa53..f880b98 100644 --- a/TTS/streamlabs_polly.py +++ b/TTS/streamlabs_polly.py @@ -1,7 +1,7 @@ import random -import os import requests from requests.exceptions import JSONDecodeError +from utils import settings voices = [ "Brian", @@ -35,11 +35,11 @@ class StreamlabsPolly: if random_voice: voice = self.randomvoice() else: - if not os.getenv("STREAMLABS_VOICE"): + if not settings.config["settings"]["tts"]["streamlabs_polly_voice"]: return ValueError( f"Please set the environment variable STREAMLABS_VOICE to a valid voice. options are: {voices}" ) - voice = str(os.getenv("STREAMLABS_VOICE")).capitalize() + voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize() body = {"voice": voice, "text": text, "service": "polly"} response = requests.post(self.url, data=body) try: @@ -56,5 +56,3 @@ class StreamlabsPolly: def randomvoice(self): return random.choice(self.voices) - -# StreamlabsPolly().run(text=str('hi hi ' * 92)[1:], filepath='hello.mp3', random_voice=True) From f5560cae6999e6a44491c85024aca68653d1831a Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:26:00 +0100 Subject: [PATCH 25/43] refactor: background credit is now a variable --- main.py | 4 ++-- utils/videos.py | 6 ++---- video_creation/background.py | 7 ++++--- video_creation/final_video.py | 12 ++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index 7b7c726..f98e8db 100755 --- a/main.py +++ b/main.py @@ -38,8 +38,8 @@ def main(POST_ID=None): length = math.ceil(length) download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_background() - chop_background_video(length) - make_final_video(number_of_comments, length, reddit_object) + credit = chop_background_video(length) + make_final_video(number_of_comments, length, reddit_object, credit) def run_many(times): diff --git a/utils/videos.py b/utils/videos.py index 6377256..ec09362 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -34,7 +34,7 @@ def check_done( return redditobj -def save_data(filename: str, reddit_title: str, reddit_id: str): +def save_data(filename: str, reddit_title: str, reddit_id: str, credit: str): """Saves the videos that have already been generated to a JSON file in video_creation/data/videos.json Args: @@ -50,9 +50,7 @@ def save_data(filename: str, reddit_title: str, reddit_id: str): payload = { "id": reddit_id, "time": str(int(time.time())), - "background_credit": settings.config["settings"]["background_credit"] - if "background_credit" in settings.config["settings"] - else "", + "background_credit": credit, "reddit_title": reddit_title, "filename": filename, } diff --git a/video_creation/background.py b/video_creation/background.py index 2654499..9fd68a1 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -1,5 +1,5 @@ import random -from os import listdir, environ +from os import listdir from pathlib import Path from random import randrange from typing import Tuple @@ -55,7 +55,7 @@ def download_background(): print_substep("Background videos downloaded successfully! 🎉", style="bold green") -def chop_background_video(video_length: int): +def chop_background_video(video_length: int) -> str: """Generates the background footage to be used in the video and writes it to assets/temp/background.mp4 Args: @@ -63,7 +63,7 @@ def chop_background_video(video_length: int): """ print_step("Finding a spot in the backgrounds video to chop...✂️") choice = random.choice(listdir("assets/backgrounds")) - environ["background_credit"] = choice.split("-")[0] + credit = choice.split("-")[0] background = VideoFileClip(f"assets/backgrounds/{choice}") @@ -81,3 +81,4 @@ def chop_background_video(video_length: int): new = video.subclip(start_time, end_time) new.write_videofile("assets/temp/background.mp4") print_substep("Background video chopped successfully!", style="bold green") + return credit diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 84698c5..9b85545 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -20,13 +20,13 @@ from rich.console import Console from utils.cleanup import cleanup from utils.console import print_step, print_substep from utils.videos import save_data - +from utils import settings console = Console() W, H = 1080, 1920 -def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): +def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, background_credit: str): """Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp Args: @@ -37,7 +37,7 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): print_step("Creating the final video 🎥") VideoFileClip.reW = lambda clip: clip.resize(width=W) VideoFileClip.reH = lambda clip: clip.resize(width=H) - opacity = os.getenv("OPACITY") + opacity = settings.config["settings"]["opacity"] background_clip = ( VideoFileClip("assets/temp/background.mp4") .without_audio() @@ -91,9 +91,9 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"]) idx = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) filename = f"{title}.mp4" - subreddit = os.getenv("SUBREDDIT") + subreddit = settings.config["reddit"]["thread"]["subreddit"] - save_data(filename, title, idx) + save_data(filename, title, idx, credit) if not exists(f"./results/{subreddit}"): print_substep("The results folder didn't exist so I made it") @@ -118,5 +118,5 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): print_substep("See result in the results folder!") print_step( - f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {os.getenv("background_credit")}' + f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_credit}' ) From 9c87cb7d950f913be5a369a1690d4456d75c942b Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:28:51 +0100 Subject: [PATCH 26/43] fix: mismatched variable name --- video_creation/final_video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 9b85545..fb54e96 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -93,7 +93,7 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, backgr filename = f"{title}.mp4" subreddit = settings.config["reddit"]["thread"]["subreddit"] - save_data(filename, title, idx, credit) + save_data(filename, title, idx, background_credit) if not exists(f"./results/{subreddit}"): print_substep("The results folder didn't exist so I made it") From ead88d93a1216b1023b00b2de92889acc10bb04a Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:27:26 +0100 Subject: [PATCH 27/43] refactor: screenshot downloader now uses toml config --- video_creation/screenshot_downloader.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index c6ed035..523966c 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -1,5 +1,4 @@ import json -import os from pathlib import Path from typing import Dict @@ -58,7 +57,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in if settings.config["reddit"]["thread"]["post_lang"]: print_substep("Translating post...") - texts_in_tl = ts.google(reddit_object["thread_title"], to_language=os.getenv("POSTLANG")) + texts_in_tl = ts.google(reddit_object["thread_title"], to_language=settings.config["reddit"]["thread"]["post_lang"]) page.evaluate( "tl_content => document.querySelector('[data-test-id=\"post-content\"] > div:nth-child(3) > div > div').textContent = tl_content", From ac28e72017da0fddd23b8a9bb4a02f4593e9b637 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:40:46 +0100 Subject: [PATCH 28/43] fix: do not use optional so as to avoid undefined values --- .config.template.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.config.template.toml b/.config.template.toml index 7f4a277..9fbf3f3 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -3,21 +3,21 @@ client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of y client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "asdfghjkl", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } -2fa = { optional = true, type = "bool", options = [ +2fa = { optional = false, type = "bool", options = [ true, false, ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true } [reddit.thread] -random = { optional = true, options = [ +random = { optional = false, 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" } -post_id = { optional = true, regex = "^((?!://|://).)*$", explanation = "Used if you want to use a specific post.", example = "urdtfx" } +post_id = { optional = false, default = "", regex = "^((?!://|://).)*$", 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 = { optional = true, explanation = "The language you would like to translate to - leave blank for none.", example = "es-cr"} +post_lang = { default = "", optional = false, explanation = "The language you would like to translate to - leave blank for none.", example = "es-cr"} [settings] allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ @@ -30,7 +30,7 @@ theme = { optional = false, default = "light", example = "dark", options = [ ], explanation = "sets the Reddit theme, either LIGHT or DARK" } times_to_run = { optional = false, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } -storymode = { optional = true, type = "bool", default = false, example = false, options = [ +storymode = { optional = false, type = "bool", default = false, example = false, options = [ true, false, ] } @@ -40,4 +40,4 @@ storymode = { optional = true, type = "bool", default = false, example = false, 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" } -choice = { optional = true, example = "streamlabspolly", explanation = "The backend used for TTS generation" } +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." } From 6a16c9f3e7a295e178857ccf5feab3e207de6394 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:46:52 +0100 Subject: [PATCH 29/43] style: format with python-black --- TTS/GTTS.py | 6 ++++- TTS/TikTok.py | 17 +++++++++----- TTS/aws_polly.py | 4 +++- TTS/engine_wrapper.py | 22 +++++++++++++----- TTS/streamlabs_polly.py | 5 ++-- main.py | 4 +++- reddit/subreddit.py | 31 ++++++++++++++++++------- utils/cleanup.py | 4 +++- utils/console.py | 9 +++++-- utils/settings.py | 28 ++++++++++++++++++---- utils/subreddit.py | 4 +++- utils/videos.py | 4 +++- video_creation/background.py | 4 +++- video_creation/final_video.py | 22 +++++++++++++----- video_creation/screenshot_downloader.py | 20 ++++++++++++---- video_creation/voices.py | 14 ++++++++--- 16 files changed, 148 insertions(+), 50 deletions(-) diff --git a/TTS/GTTS.py b/TTS/GTTS.py index 27b6934..31e29df 100644 --- a/TTS/GTTS.py +++ b/TTS/GTTS.py @@ -12,7 +12,11 @@ class GTTS: self.voices = [] def run(self, text, filepath): - tts = gTTS(text=text, lang=settings.config["reddit"]["thread"]["post_lang"] or "en", slow=False) + tts = gTTS( + text=text, + lang=settings.config["reddit"]["thread"]["post_lang"] or "en", + slow=False, + ) tts.save(filepath) def randomvoice(self): diff --git a/TTS/TikTok.py b/TTS/TikTok.py index 4583358..6a116d7 100644 --- a/TTS/TikTok.py +++ b/TTS/TikTok.py @@ -62,9 +62,7 @@ noneng = [ class TikTok: # TikTok Text-to-Speech Wrapper def __init__(self): - self.URI_BASE = ( - "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker=" - ) + self.URI_BASE = "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker=" self.max_chars = 300 self.voices = {"human": human, "nonhuman": nonhuman, "noneng": noneng} @@ -75,10 +73,15 @@ class TikTok: # TikTok Text-to-Speech Wrapper voice = ( self.randomvoice() if random_voice - else (settings.config["settings"]["tts"]["tiktok_voice"] or random.choice(self.voices["human"])) + else ( + settings.config["settings"]["tts"]["tiktok_voice"] + or random.choice(self.voices["human"]) + ) ) try: - r = requests.post(f"{self.URI_BASE}{voice}&req_text={text}&speaker_map_type=0") + r = requests.post( + f"{self.URI_BASE}{voice}&req_text={text}&speaker_map_type=0" + ) except requests.exceptions.SSLError: # https://stackoverflow.com/a/47475019/18516611 session = requests.Session() @@ -86,7 +89,9 @@ class TikTok: # TikTok Text-to-Speech Wrapper adapter = HTTPAdapter(max_retries=retry) session.mount("http://", adapter) session.mount("https://", adapter) - r = session.post(f"{self.URI_BASE}{voice}&req_text={text}&speaker_map_type=0") + r = session.post( + f"{self.URI_BASE}{voice}&req_text={text}&speaker_map_type=0" + ) # print(r.text) vstr = [r.json()["data"]["v_str"]][0] b64d = base64.b64decode(vstr) diff --git a/TTS/aws_polly.py b/TTS/aws_polly.py index 94143db..bf8ec1e 100644 --- a/TTS/aws_polly.py +++ b/TTS/aws_polly.py @@ -39,7 +39,9 @@ class AWSPolly: return ValueError( f"Please set the environment variable AWS_VOICE to a valid voice. options are: {voices}" ) - voice = str(settings.config["settings"]["tts"]["aws_polly_voice"]).capitalize() + voice = str( + settings.config["settings"]["tts"]["aws_polly_voice"] + ).capitalize() try: # Request speech synthesis response = polly.synthesize_speech( diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index c520ab0..e3cc04b 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -56,11 +56,16 @@ class TTSEngine: print_step("Saving Text to MP3 files...") self.call_tts("title", self.reddit_object["thread_title"]) - if self.reddit_object["thread_post"] != "" and settings.config["settings"]["storymode"] == True: + if ( + self.reddit_object["thread_post"] != "" + and settings.config["settings"]["storymode"] == True + ): self.call_tts("posttext", self.reddit_object["thread_post"]) 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: break @@ -76,7 +81,9 @@ class TTSEngine: split_files = [] split_text = [ x.group().strip() - for x in re.finditer(rf" *((.{{0,{self.tts_module.max_chars}}})(\.|.$))", text) + for x in re.finditer( + rf" *((.{{0,{self.tts_module.max_chars}}})(\.|.$))", text + ) ] idy = None @@ -94,12 +101,14 @@ class TTSEngine: Path(name).unlink() # for i in range(0, idy + 1): - # print(f"Cleaning up {self.path}/{idx}-{i}.part.mp3") + # print(f"Cleaning up {self.path}/{idx}-{i}.part.mp3") - # Path(f"{self.path}/{idx}-{i}.part.mp3").unlink() + # Path(f"{self.path}/{idx}-{i}.part.mp3").unlink() def call_tts(self, filename: str, text: str): - self.tts_module.run(text=process_text(text), filepath=f"{self.path}/{filename}.mp3") + self.tts_module.run( + text=process_text(text), filepath=f"{self.path}/{filename}.mp3" + ) # try: # self.length += MP3(f"{self.path}/{filename}.mp3").info.length # except (MutagenError, HeaderNotFoundError): @@ -108,6 +117,7 @@ class TTSEngine: self.length += clip.duration clip.close() + def process_text(text: str): lang = settings.config["reddit"]["thread"]["post_lang"] new_text = sanitize_text(text) diff --git a/TTS/streamlabs_polly.py b/TTS/streamlabs_polly.py index f880b98..e9e6358 100644 --- a/TTS/streamlabs_polly.py +++ b/TTS/streamlabs_polly.py @@ -39,7 +39,9 @@ class StreamlabsPolly: return ValueError( f"Please set the environment variable STREAMLABS_VOICE to a valid voice. options are: {voices}" ) - voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize() + voice = str( + settings.config["settings"]["tts"]["streamlabs_polly_voice"] + ).capitalize() body = {"voice": voice, "text": text, "service": "polly"} response = requests.post(self.url, data=body) try: @@ -55,4 +57,3 @@ class StreamlabsPolly: def randomvoice(self): return random.choice(self.voices) - diff --git a/main.py b/main.py index f98e8db..c7079d5 100755 --- a/main.py +++ b/main.py @@ -59,7 +59,9 @@ if __name__ == "__main__": run_many(config["settings"]["times_to_run"]) elif len(config["reddit"]["thread"]["post_id"].split("+")) > 1: - for index, post_id in enumerate(config["reddit"]["thread"]["post_id"].split("+")): + for index, post_id in enumerate( + config["reddit"]["thread"]["post_id"].split("+") + ): index += 1 print_step( f'on the {index}{("st" if index%10 == 1 else ("nd" if index%10 == 2 else ("rd" if index%10 == 3 else "th")))} post of {len(config["reddit"]["thread"]["post_id"].split("+"))}' diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 40c82a1..7583653 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -18,7 +18,9 @@ def get_subreddit_threads(POST_ID: str): content = {} if settings.config["reddit"]["creds"]["2fa"] == True: - print("\nEnter your two-factor authentication code from your authenticator app.\n") + print( + "\nEnter your two-factor authentication code from your authenticator app.\n" + ) code = input("> ") print() pw = settings.config["reddit"]["creds"]["password"] @@ -29,7 +31,7 @@ def get_subreddit_threads(POST_ID: str): if username.casefold().startswith("u/"): username = username[2:] reddit = praw.Reddit( - client_id= settings.config["reddit"]["creds"]["client_id"], + client_id=settings.config["reddit"]["creds"]["client_id"], client_secret=settings.config["reddit"]["creds"]["client_secret"], user_agent="Accessing Reddit threads", username=username, @@ -39,10 +41,14 @@ def get_subreddit_threads(POST_ID: str): # Ask user for subreddit input print_step("Getting subreddit threads...") - if not settings.config["reddit"]["thread"]["subreddit"]: # note to user. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython") + if not settings.config["reddit"]["thread"][ + "subreddit" + ]: # note to user. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython") try: subreddit = reddit.subreddit( - re.sub(r"r\/", "", input("What subreddit would you like to pull from? ")) + re.sub( + r"r\/", "", input("What subreddit would you like to pull from? ") + ) # removes the r/ from the input ) except ValueError: @@ -52,7 +58,9 @@ def get_subreddit_threads(POST_ID: str): 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 subreddit_choice.casefold().startswith( + "r/" + ): # removes the r/ from the input subreddit_choice = subreddit_choice[2:] subreddit = reddit.subreddit( subreddit_choice @@ -60,8 +68,13 @@ def get_subreddit_threads(POST_ID: str): if POST_ID: # would only be called if there are multiple queued posts submission = reddit.submission(id=POST_ID) - elif settings.config["reddit"]["thread"]["post_id"] and len(settings.config["reddit"]["thread"]["post_id"].split("+")) == 1: - submission = reddit.submission(id=settings.config["reddit"]["thread"]["post_id"]) + elif ( + settings.config["reddit"]["thread"]["post_id"] + and len(settings.config["reddit"]["thread"]["post_id"].split("+")) == 1 + ): + submission = reddit.submission( + id=settings.config["reddit"]["thread"]["post_id"] + ) else: threads = subreddit.hot(limit=25) @@ -90,7 +103,9 @@ def get_subreddit_threads(POST_ID: str): if top_level_comment.body in ["[removed]", "[deleted]"]: continue # # see https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/78 if not top_level_comment.stickied: - if len(top_level_comment.body) <= int(settings.config["reddit"]["thread"]["max_comment_length"]): + if len(top_level_comment.body) <= int( + settings.config["reddit"]["thread"]["max_comment_length"] + ): if ( top_level_comment.author is not None ): # if errors occur with this change to if not. diff --git a/utils/cleanup.py b/utils/cleanup.py index ef4fc44..44629a9 100644 --- a/utils/cleanup.py +++ b/utils/cleanup.py @@ -10,7 +10,9 @@ def cleanup() -> int: """ 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/console.py b/utils/console.py index 310247d..b8faa80 100644 --- a/utils/console.py +++ b/utils/console.py @@ -49,7 +49,10 @@ def handle_input( optional=False, ): if optional: - console.print(message + "\n[green]This is an optional value. Do you want to skip it? (y/n)") + console.print( + message + + "\n[green]This is an optional value. Do you want to skip it? (y/n)" + ) if input().casefold().startswith("y"): return None if default is not NotImplemented: @@ -84,7 +87,9 @@ def handle_input( continue elif match != "" and re.match(match, user_input) is None: console.print( - "[red]" + err_message + "\nAre you absolutely sure it's correct?(y/n)" + "[red]" + + err_message + + "\nAre you absolutely sure it's correct?(y/n)" ) if input().casefold().startswith("y"): break diff --git a/utils/settings.py b/utils/settings.py index d674974..c77e4ae 100755 --- a/utils/settings.py +++ b/utils/settings.py @@ -54,7 +54,11 @@ def check(value, checks, name): and not hasattr(value, "__iter__") and ( ("nmin" in checks and checks["nmin"] is not None and value < checks["nmin"]) - or ("nmax" in checks and checks["nmax"] is not None and value > checks["nmax"]) + or ( + "nmax" in checks + and checks["nmax"] is not None + and value > checks["nmax"] + ) ) ): incorrect = True @@ -62,8 +66,16 @@ def check(value, checks, name): not incorrect and hasattr(value, "__iter__") and ( - ("nmin" in checks and checks["nmin"] is not None and len(value) < checks["nmin"]) - or ("nmax" in checks and checks["nmax"] is not None and len(value) > checks["nmax"]) + ( + "nmin" in checks + and checks["nmin"] is not None + and len(value) < checks["nmin"] + ) + or ( + "nmax" in checks + and checks["nmax"] is not None + and len(value) > checks["nmax"] + ) ) ): incorrect = True @@ -71,7 +83,11 @@ def check(value, checks, name): if incorrect: value = handle_input( message=( - (("[blue]Example: " + str(checks["example"]) + "\n") if "example" in checks else "") + ( + ("[blue]Example: " + str(checks["example"]) + "\n") + if "example" in checks + else "" + ) + "[red]" + ("Non-optional ", "Optional ")[ "optional" in checks and checks["optional"] is True @@ -84,7 +100,9 @@ def check(value, checks, name): check_type=eval(checks["type"]) if "type" in checks else False, default=checks["default"] if "default" in checks else NotImplemented, match=checks["regex"] if "regex" in checks else "", - err_message=checks["input_error"] if "input_error" in checks else "Incorrect input", + err_message=checks["input_error"] + if "input_error" in checks + else "Incorrect input", nmin=checks["nmin"] if "nmin" in checks else None, nmax=checks["nmax"] if "nmax" in checks else None, oob_error=checks["oob_error"] diff --git a/utils/subreddit.py b/utils/subreddit.py index 140f4d3..7647454 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -15,7 +15,9 @@ def get_subreddit_undone(submissions: list, subreddit): """ # recursively checks if the top submission in the list was already done. - with open("./video_creation/data/videos.json", "r", encoding="utf-8") as done_vids_raw: + with open( + "./video_creation/data/videos.json", "r", encoding="utf-8" + ) as done_vids_raw: done_videos = json.load(done_vids_raw) for submission in submissions: if already_done(done_videos, submission): diff --git a/utils/videos.py b/utils/videos.py index ec09362..38184e4 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -20,7 +20,9 @@ def check_done( Returns: Dict[str]|None: Reddit object in args """ - with open("./video_creation/data/videos.json", "r", encoding="utf-8") as done_vids_raw: + with open( + "./video_creation/data/videos.json", "r", encoding="utf-8" + ) as done_vids_raw: done_videos = json.load(done_vids_raw) for video in done_videos: if video["id"] == str(redditobj): diff --git a/video_creation/background.py b/video_creation/background.py index 9fd68a1..494c7d2 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -52,7 +52,9 @@ def download_background(): "assets/backgrounds", filename=f"{credit}-{filename}" ) - print_substep("Background videos downloaded successfully! 🎉", style="bold green") + print_substep( + "Background videos downloaded successfully! 🎉", style="bold green" + ) def chop_background_video(video_length: int) -> str: diff --git a/video_creation/final_video.py b/video_creation/final_video.py index fb54e96..8fccd38 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -21,12 +21,15 @@ from utils.cleanup import cleanup from utils.console import print_step, print_substep from utils.videos import save_data from utils import settings + console = Console() W, H = 1080, 1920 -def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, background_credit: str): +def make_final_video( + number_of_clips: int, length: int, reddit_obj: dict, background_credit: str +): """Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp Args: @@ -46,7 +49,9 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, backgr ) # Gather all audio clips - audio_clips = [AudioFileClip(f"assets/temp/mp3/{i}.mp3") for i in range(number_of_clips)] + audio_clips = [ + AudioFileClip(f"assets/temp/mp3/{i}.mp3") for i in range(number_of_clips) + ] audio_clips.insert(0, AudioFileClip("assets/temp/mp3/title.mp3")) audio_concat = concatenate_audioclips(audio_clips) audio_composite = CompositeAudioClip([audio_concat]) @@ -63,7 +68,7 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, backgr .set_duration(audio_clips[0].duration) .set_position("center") .resize(width=W - 100) - .set_opacity(new_opacity) + .set_opacity(new_opacity), ) for i in range(0, number_of_clips): @@ -85,13 +90,15 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, backgr # .set_opacity(float(opacity)), # ) # else: - image_concat = concatenate_videoclips(image_clips).set_position(("center", "center")) + image_concat = concatenate_videoclips(image_clips).set_position( + ("center", "center") + ) image_concat.audio = audio_composite final = CompositeVideoClip([background_clip, image_concat]) title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"]) idx = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) filename = f"{title}.mp4" - subreddit = settings.config["reddit"]["thread"]["subreddit"] + subreddit = settings.config["reddit"]["thread"]["subreddit"] save_data(filename, title, idx, background_credit) @@ -108,7 +115,10 @@ def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, backgr threads=multiprocessing.cpu_count(), ) ffmpeg_tools.ffmpeg_extract_subclip( - "assets/temp/temp.mp4", 0, final.duration, targetname=f"results/{subreddit}/{filename}" + "assets/temp/temp.mp4", + 0, + final.duration, + targetname=f"results/{subreddit}/{filename}", ) # os.remove("assets/temp/temp.mp4") diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index 523966c..efc48bd 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -35,9 +35,13 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in context = browser.new_context() if settings.config["settings"]["theme"] == "dark": - cookie_file = open("./video_creation/data/cookie-dark-mode.json", encoding="utf-8") + cookie_file = open( + "./video_creation/data/cookie-dark-mode.json", encoding="utf-8" + ) else: - cookie_file = open("./video_creation/data/cookie-light-mode.json", encoding="utf-8") + cookie_file = open( + "./video_creation/data/cookie-light-mode.json", encoding="utf-8" + ) cookies = json.load(cookie_file) context.add_cookies(cookies) # load preference cookies # Get the thread screenshot @@ -57,7 +61,10 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in if settings.config["reddit"]["thread"]["post_lang"]: print_substep("Translating post...") - texts_in_tl = ts.google(reddit_object["thread_title"], to_language=settings.config["reddit"]["thread"]["post_lang"]) + texts_in_tl = ts.google( + reddit_object["thread_title"], + to_language=settings.config["reddit"]["thread"]["post_lang"], + ) page.evaluate( "tl_content => document.querySelector('[data-test-id=\"post-content\"] > div:nth-child(3) > div > div').textContent = tl_content", @@ -66,7 +73,9 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in else: print_substep("Skipping translation...") - page.locator('[data-test-id="post-content"]').screenshot(path="assets/temp/png/title.png") + page.locator('[data-test-id="post-content"]').screenshot( + path="assets/temp/png/title.png" + ) if storymode: page.locator('[data-click-id="text"]').screenshot( @@ -89,7 +98,8 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in if settings.config["reddit"]["thread"]["post_lang"]: comment_tl = ts.google( - comment["comment_body"], to_language=settings.config["reddit"]["thread"]["post_lang"] + comment["comment_body"], + to_language=settings.config["reddit"]["thread"]["post_lang"], ) page.evaluate( '([tl_content, tl_id]) => document.querySelector(`#t1_${tl_id} > div:nth-child(2) > div > div[data-testid="comment"] > div`).textContent = tl_content', diff --git a/video_creation/voices.py b/video_creation/voices.py index 4224d15..4bbd5d7 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -35,7 +35,9 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]: voice = settings.config["settings"]["tts"]["choice"] if voice.casefold() in map(lambda _: _.casefold(), TTSProviders): - text_to_mp3 = TTSEngine(get_case_insensitive_key_value(TTSProviders, voice), reddit_obj) + text_to_mp3 = TTSEngine( + get_case_insensitive_key_value(TTSProviders, voice), reddit_obj + ) else: while True: print_step("Please choose one of the following TTS providers: ") @@ -44,13 +46,19 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]: if choice.casefold() in map(lambda _: _.casefold(), TTSProviders): break print("Unknown Choice") - text_to_mp3 = TTSEngine(get_case_insensitive_key_value(TTSProviders, choice), reddit_obj) + text_to_mp3 = TTSEngine( + get_case_insensitive_key_value(TTSProviders, choice), reddit_obj + ) return text_to_mp3.run() def get_case_insensitive_key_value(input_dict, key): return next( - (value for dict_key, value in input_dict.items() if dict_key.lower() == key.lower()), + ( + value + for dict_key, value in input_dict.items() + if dict_key.lower() == key.lower() + ), None, ) From f5bb3b6129d5e9d3b322c253b5e6fbea9c8560b4 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 00:58:45 +0100 Subject: [PATCH 30/43] chore: update requirements --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 687f952..7ef2ad8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,10 @@ boto3==1.24.12 botocore==1.27.22 gTTS==2.2.4 moviepy==1.0.3 -mutagen==1.45.1 playwright==1.23.0 praw==7.6.0 -python-dotenv==0.20.0 pytube==12.1.0 requests==2.28.1 rich==12.4.4 +toml==0.10.2 translators==5.3.1 From 53c98f5d512a7beb415632cd4dbca465d309b1ef Mon Sep 17 00:00:00 2001 From: Jason <66544866+JasonLovesDoggo@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:31:21 -0400 Subject: [PATCH 31/43] Revert "Added translation for filename" --- video_creation/final_video.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index be42d86..d706361 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -4,7 +4,6 @@ import os import re from os.path import exists from typing import Dict -import translators as ts from moviepy.editor import ( VideoFileClip, @@ -36,14 +35,7 @@ def name_normalize( name = re.sub(r'([0-9]+)\s?\/\s?([0-9]+)', r'\1 of \2', name) name = re.sub(r'(\w+)\s?\/\s?(\w+)', r'\1 or \2', name) name = re.sub(r'\/', r'', name) - - if os.getenv("POSTLANG") != "": - print_substep("Translating filename...") - translated_name = ts.google(name, to_language=os.getenv("POSTLANG")) - return translated_name - - else: - return name + return name def make_final_video(number_of_clips: int, length: int, reddit_obj: dict): From 869bfd874a46ad53439581c387842710031af6f7 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 11:47:44 +0000 Subject: [PATCH 32/43] docs: clarify config in readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc6237c..7f10287 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,13 @@ The only original thing being done is the editing and gathering of all materials 1. Clone this repository 2. Run `pip install -r requirements.txt` -3. Run `playwright install` and `playwright install-deps`. (if this fails try adding python -m to the front of the command) +3. Run `python -m playwright install` and `python -m playwright install-deps` 4. Run `python main.py` - required\*\*), visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps) TL;DR set up an app that is a "script". -5. Enjoy 😎 +5. Visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps), and set up an app that is a "script". +6. The bot will ask you to fill in your details to connect to the Reddit API, and configure the bot to your liking +7. Enjoy 😎 +8. If you need to reconfigure the bot, simply open the `config.toml` file and delete the lines that need to be changed. On the next run of the bot, it will help you reconfigure those options. (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) From 96b57ff4119109ba14354ae5af9893b3cfa76fe9 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 13:14:07 +0100 Subject: [PATCH 33/43] chore: add python-black action --- .github/workflows/lint.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..b04fb15 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,10 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: psf/black@stable From 5dbb35128df02225a2d2a423f86deb2dbaef44cc Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 13:14:26 +0100 Subject: [PATCH 34/43] chore: update codeql-analysis to run on develop --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 238dad4..ec78b1a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,10 +14,10 @@ name: "CodeQL" on: push: - branches: [ "master" ] + branches: [ "master", "develop" ] pull_request: # The branches below must be a subset of the branches above - branches: [ "master" ] + branches: [ "master", "develop" ] schedule: - cron: '16 14 * * 3' From d0a76972567bfcaebb3f0f069bf29b5e3c06e5da Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 14:40:56 +0100 Subject: [PATCH 35/43] fix: optionals return default or empty string --- .config.template.toml | 10 +++++----- utils/console.py | 2 +- utils/settings.py | 32 ++++++++++++-------------------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/.config.template.toml b/.config.template.toml index 9fbf3f3..e1089e3 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -3,21 +3,21 @@ client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of y client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "asdfghjkl", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } -2fa = { optional = false, type = "bool", options = [ +2fa = { optional = true, type = "bool", options = [ true, false, ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true } [reddit.thread] -random = { optional = false, options = [ +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" } -post_id = { optional = false, default = "", regex = "^((?!://|://).)*$", explanation = "Used if you want to use a specific post.", example = "urdtfx" } +post_id = { optional = true, default = "", regex = "^((?!://|://).)*$", 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 = false, explanation = "The language you would like to translate to - leave blank for none.", example = "es-cr"} +post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr"} [settings] allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ @@ -30,7 +30,7 @@ theme = { optional = false, default = "light", example = "dark", options = [ ], explanation = "sets the Reddit theme, either LIGHT or DARK" } times_to_run = { optional = false, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } -storymode = { optional = false, type = "bool", default = false, example = false, options = [ +storymode = { optional = true, type = "bool", default = false, example = false, options = [ true, false, ] } diff --git a/utils/console.py b/utils/console.py index b8faa80..1ffa11c 100644 --- a/utils/console.py +++ b/utils/console.py @@ -54,7 +54,7 @@ def handle_input( + "\n[green]This is an optional value. Do you want to skip it? (y/n)" ) if input().casefold().startswith("y"): - return None + return default if default is not NotImplemented else "" if default is not NotImplemented: console.print( "[green]" diff --git a/utils/settings.py b/utils/settings.py index c77e4ae..afd2ec0 100755 --- a/utils/settings.py +++ b/utils/settings.py @@ -23,11 +23,10 @@ def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): def check(value, checks, name): - + def get_check_value(key, default_result): + return checks[key] if key in checks else default_result incorrect = False if value == {}: - if skip_opt and "optional" in checks and checks["optional"] is True: - return None incorrect = True if not incorrect and "type" in checks: try: @@ -96,20 +95,16 @@ def check(value, checks, name): + "[#C0CAF5 bold]" + str(name) + "[#F7768E bold]=", - extra_info=checks["explanation"] if "explanation" in checks else "", - check_type=eval(checks["type"]) if "type" in checks else False, - default=checks["default"] if "default" in checks else NotImplemented, - match=checks["regex"] if "regex" in checks else "", - err_message=checks["input_error"] - if "input_error" in checks - else "Incorrect input", - nmin=checks["nmin"] if "nmin" in checks else None, - nmax=checks["nmax"] if "nmax" in checks else None, - oob_error=checks["oob_error"] - if "oob_error" in checks - else "Input out of bounds(Value too high/low/long/short)", - options=checks["options"] if "options" in checks else None, - optional=checks["optional"] if "optional" in checks else False, + extra_info=get_check_value("explanation", ""), + check_type=eval(get_check_value("type", "False")), + default=get_check_value("default", NotImplemented), + match=get_check_value("regex", ""), + err_message=get_check_value("input_error", "Incorrect input"), + nmin=get_check_value("nmin", None), + nmax=get_check_value("nmax", None), + oob_error=get_check_value("oob_error", "Input out of bounds(Value too high/low/long/short)"), + options=get_check_value("options", None), + optional=get_check_value("optional", False), ) return value @@ -125,8 +120,6 @@ def crawl_and_check(obj: dict, path: list, checks: dict = {}, name=""): def check_vars(path, checks): global config - global skip_opt - skip_opt = "skip_opt" in config crawl_and_check(config, path, checks) @@ -185,7 +178,6 @@ If you see any prompts, that means that you have unset/incorrectly set variables """ ) crawl(template, check_vars) - config["skip_opt"] = True with open(config_file, "w") as f: toml.dump(config, f) return config From 4ed5c58ab264cebac53756d7b21d2e38562f8523 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 14:42:16 +0100 Subject: [PATCH 36/43] style: formatted with black --- utils/settings.py | 5 ++++- video_creation/final_video.py | 17 ++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/utils/settings.py b/utils/settings.py index afd2ec0..07cebd4 100755 --- a/utils/settings.py +++ b/utils/settings.py @@ -25,6 +25,7 @@ def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): def check(value, checks, name): def get_check_value(key, default_result): return checks[key] if key in checks else default_result + incorrect = False if value == {}: incorrect = True @@ -102,7 +103,9 @@ def check(value, checks, name): err_message=get_check_value("input_error", "Incorrect input"), nmin=get_check_value("nmin", None), nmax=get_check_value("nmax", None), - oob_error=get_check_value("oob_error", "Input out of bounds(Value too high/low/long/short)"), + oob_error=get_check_value( + "oob_error", "Input out of bounds(Value too high/low/long/short)" + ), options=get_check_value("options", None), optional=get_check_value("optional", False), ) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 5938235..1c5e781 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -26,15 +26,14 @@ console = Console() W, H = 1080, 1920 -def name_normalize( - name: str -) -> str: - name = re.sub(r'[?\\"%*:|<>]', '', name) - name = re.sub(r'( [w,W]\s?\/\s?[o,O,0])', r' without', name) - name = re.sub(r'( [w,W]\s?\/)', r' with', name) - name = re.sub(r'([0-9]+)\s?\/\s?([0-9]+)', r'\1 of \2', name) - name = re.sub(r'(\w+)\s?\/\s?(\w+)', r'\1 or \2', name) - name = re.sub(r'\/', r'', name) + +def name_normalize(name: str) -> str: + name = re.sub(r'[?\\"%*:|<>]', "", name) + name = re.sub(r"( [w,W]\s?\/\s?[o,O,0])", r" without", name) + name = re.sub(r"( [w,W]\s?\/)", r" with", name) + name = re.sub(r"([0-9]+)\s?\/\s?([0-9]+)", r"\1 of \2", name) + name = re.sub(r"(\w+)\s?\/\s?(\w+)", r"\1 or \2", name) + name = re.sub(r"\/", r"", name) return name From cebbbf72cb2a75170e5ea24bd12550cc189c1d5e Mon Sep 17 00:00:00 2001 From: Tomovic Date: Wed, 6 Jul 2022 16:19:36 +0200 Subject: [PATCH 37/43] Added translation for filename TOML compatible --- video_creation/final_video.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 5938235..b56d231 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -4,6 +4,7 @@ import os import re from os.path import exists from typing import Dict +import translators as ts from moviepy.editor import ( VideoFileClip, @@ -35,7 +36,15 @@ def name_normalize( name = re.sub(r'([0-9]+)\s?\/\s?([0-9]+)', r'\1 of \2', name) name = re.sub(r'(\w+)\s?\/\s?(\w+)', r'\1 or \2', name) name = re.sub(r'\/', r'', name) - return name + + lang = settings.config["reddit"]["thread"]["post_lang"] + if lang != "": + print_substep("Translating filename...") + translated_name = ts.google(name, to_language = lang) + return translated_name + + else: + return name def make_final_video( From 71eca6991215e1e8538c03c789b4893bacc0eef3 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 20:08:45 +0000 Subject: [PATCH 38/43] fix: handle non value for post lang --- video_creation/final_video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 1080f66..4f73cf8 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -37,7 +37,7 @@ def name_normalize(name: str) -> str: name = re.sub(r"\/", r"", name) lang = settings.config["reddit"]["thread"]["post_lang"] - if lang != "": + if lang: print_substep("Translating filename...") translated_name = ts.google(name, to_language = lang) return translated_name From 4d7db77fff3097451a937ced47bf88d3655ec2e5 Mon Sep 17 00:00:00 2001 From: Callum Leslie Date: Wed, 6 Jul 2022 23:14:43 +0100 Subject: [PATCH 39/43] style: reformat with python-black --- video_creation/final_video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 4f73cf8..42f13b4 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -35,11 +35,11 @@ def name_normalize(name: str) -> str: name = re.sub(r"([0-9]+)\s?\/\s?([0-9]+)", r"\1 of \2", name) name = re.sub(r"(\w+)\s?\/\s?(\w+)", r"\1 or \2", name) name = re.sub(r"\/", r"", name) - + lang = settings.config["reddit"]["thread"]["post_lang"] if lang: print_substep("Translating filename...") - translated_name = ts.google(name, to_language = lang) + translated_name = ts.google(name, to_language=lang) return translated_name else: From b82459166b9810c9aaeae627e1d3cac5f8fc891c Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 6 Jul 2022 19:17:55 -0400 Subject: [PATCH 40/43] docs: improved examples in .config.template.toml fix: mutability issue in settings.py style: added autocomplete support for the settings.config var style: fixed autocomplete issues overall --- .config.template.toml | 8 ++--- .github/workflows/stale.yml | 40 ------------------------- utils/settings.py | 13 ++++---- utils/videos.py | 4 +-- video_creation/screenshot_downloader.py | 4 +-- video_creation/voices.py | 2 +- 6 files changed, 15 insertions(+), 56 deletions(-) delete mode 100644 .github/workflows/stale.yml diff --git a/.config.template.toml b/.config.template.toml index e1089e3..1bde264 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -1,7 +1,7 @@ [reddit.creds] client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The ID should be over 12 and under 30 characters, double check your input." } client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } -username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "asdfghjkl", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } +username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "JasonLovesDoggo", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } 2fa = { optional = true, type = "bool", options = [ true, @@ -15,7 +15,7 @@ random = { optional = true, options = [ 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" } -post_id = { optional = true, default = "", regex = "^((?!://|://).)*$", explanation = "Used if you want to use a specific post.", example = "urdtfx" } +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"} @@ -24,7 +24,7 @@ allow_nsfw = { optional = false, type = "bool", default = false, example = false true, false, ], explanation = "Whether to allow NSFW content, True or False" } -theme = { optional = false, default = "light", example = "dark", options = [ +theme = { optional = false, default = "dark", example = "light", options = [ "dark", "light", ], explanation = "sets the Reddit theme, either LIGHT or DARK" } @@ -37,7 +37,7 @@ storymode = { optional = true, type = "bool", default = false, example = false, [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" } 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" } -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." } diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 40f2245..0000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: 'Stale issue handler' -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@main - id: stale-issue - name: stale-issue - with: - stale-issue-message: 'This issue is stale because it has been open 7 days with no activity. Remove stale label or comment, or this will be closed in 10 days.' - close-issue-message: 'Issue closed due to being stale. Please reopen if issue persists in latest version.' - days-before-stale: 7 - days-before-close: 10 - stale-issue-label: 'stale' - close-issue-label: 'outdated' - exempt-issue-labels: 'enhancement,keep,blocked' - exempt-all-issue-milestones: true - operations-per-run: 300 - remove-stale-when-updated: true - - - uses: actions/stale@main - id: stale-pr - name: stale-pr - with: - stale-pr-message: 'This pull request is stale as it has been open for 7 days with no activity. Remove stale label or comment, or this will be closed in 10 days.' - close-pr-message: 'Pull request closed due to being stale.' - days-before-stale: 7 - days-before-close: 10 - close-pr-label: 'outdated' - stale-pr-label: 'stale' - exempt-pr-labels: 'keep,blocked,before next release,after next release' - exempt-all-pr-milestones: true - operations-per-run: 300 - remove-stale-when-updated: true - diff --git a/utils/settings.py b/utils/settings.py index 07cebd4..7c2b05d 100755 --- a/utils/settings.py +++ b/utils/settings.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# import os import toml from rich.console import Console import re @@ -8,13 +7,13 @@ from typing import Tuple, Dict from utils.console import handle_input -# from console import handle_input - console = Console() +config = dict # autocomplete - -def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path: list = []): +def crawl(obj: dict, func=lambda x, y: print(x, y, end="\n"), path=None): + if path is None: # path Default argument value is mutable + path = [] for key in obj.keys(): if type(obj[key]) is dict: crawl(obj[key], func, path + [key]) @@ -138,7 +137,7 @@ def check_toml(template_file, config_file) -> Tuple[bool, Dict]: return False try: config = toml.load(config_file) - except (toml.TomlDecodeError): + except toml.TomlDecodeError: console.print( f"""[blue]Couldn't read {config_file}. Overwrite it?(y/n)""" @@ -155,7 +154,7 @@ Overwrite it?(y/n)""" f"[red bold]Failed to overwrite {config_file}. Giving up.\nSuggestion: check {config_file} permissions for the user." ) return False - except (FileNotFoundError): + except FileNotFoundError: console.print( f"""[blue]Couldn't find {config_file} Creating it now.""" diff --git a/utils/videos.py b/utils/videos.py index 38184e4..4e36729 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -15,10 +15,10 @@ def check_done( """Checks if the chosen post has already been generated Args: - redditobj (Dict[str]): Reddit object gotten from reddit/subreddit.py + redditobj (Submission): Reddit object gotten from reddit/subreddit.py Returns: - Dict[str]|None: Reddit object in args + Submission|None: Reddit object in args """ with open( "./video_creation/data/videos.json", "r", encoding="utf-8" diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index efc48bd..9dbb3d8 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -20,8 +20,8 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in """Downloads screenshots of reddit posts as seen on the web. Downloads to assets/temp/png Args: - reddit_object (Dict[str]): Reddit object received from reddit/subreddit.py - screenshot_num (int): Number of screenshots to downlaod + reddit_object (Dict): Reddit object received from reddit/subreddit.py + screenshot_num (int): Number of screenshots to download """ print_step("Downloading screenshots of reddit posts...") diff --git a/video_creation/voices.py b/video_creation/voices.py index 4bbd5d7..e6e1045 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -27,7 +27,7 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]: """Saves text to MP3 files. Args: - reddit_obj (dict[str]): Reddit object received from reddit API in reddit/subreddit.py + reddit_obj (): Reddit object received from reddit API in reddit/subreddit.py Returns: tuple[int,int]: (total length of the audio, the number of comments audio was generated for) From e3c74a2b86ed651d1cdb0aecf190e4d4e2863678 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 6 Jul 2022 19:18:56 -0400 Subject: [PATCH 41/43] fixed local issue --- .github/workflows/stale.yml | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..b2f6cac --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,39 @@ +name: 'Stale issue handler' +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@main + id: stale-issue + name: stale-issue + with: + stale-issue-message: 'This issue is stale because it has been open 7 days with no activity. Remove stale label or comment, or this will be closed in 10 days.' + close-issue-message: 'Issue closed due to being stale. Please reopen if issue persists in latest version.' + days-before-stale: 7 + days-before-close: 10 + stale-issue-label: 'stale' + close-issue-label: 'outdated' + exempt-issue-labels: 'enhancement,keep,blocked' + exempt-all-issue-milestones: true + operations-per-run: 300 + remove-stale-when-updated: true + + - uses: actions/stale@main + id: stale-pr + name: stale-pr + with: + stale-pr-message: 'This pull request is stale as it has been open for 7 days with no activity. Remove stale label or comment, or this will be closed in 10 days.' + close-pr-message: 'Pull request closed due to being stale.' + days-before-stale: 7 + days-before-close: 10 + close-pr-label: 'outdated' + stale-pr-label: 'stale' + exempt-pr-labels: 'keep,blocked,before next release,after next release' + exempt-all-pr-milestones: true + operations-per-run: 300 + remove-stale-when-updated: true From 0b6e6a4c140f068e113d65431598ca9b434a197d Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 6 Jul 2022 22:29:47 -0400 Subject: [PATCH 42/43] added toml config for #693 --- .config.template.toml | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/.config.template.toml b/.config.template.toml index 1bde264..8775357 100644 --- a/.config.template.toml +++ b/.config.template.toml @@ -3,41 +3,36 @@ client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of y client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "JasonLovesDoggo", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } -2fa = { optional = true, type = "bool", options = [ - true, - false, +2fa = { optional = true, type = "bool", options = [true, + false, ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true } [reddit.thread] -random = { optional = true, options = [ - true, - false, +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" } 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"} +post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr" } [settings] -allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ - true, - false, +allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [true, + false, ], explanation = "Whether to allow NSFW content, True or False" } -theme = { optional = false, default = "dark", example = "light", options = [ - "dark", - "light", +theme = { optional = false, default = "dark", example = "light", options = ["dark", + "light", ], explanation = "sets the Reddit theme, either LIGHT or DARK" } times_to_run = { optional = false, default = 1, example = 2, explanation = "used if you want to run multiple times. set to an int e.g. 4 or 29 or 1", type = "int", nmin = 1, oob_error = "It's very hard to run something less than once." } opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets the opacity of the comments when overlayed over the background", type = "float", nmin = 0, nmax = 1, oob_error = "The opacity HAS to be between 0 and 1", input_error = "The opacity HAS to be a decimal number between 0 and 1" } -storymode = { optional = true, type = "bool", default = false, example = false, options = [ - true, - false, +storymode = { optional = true, type = "bool", default = false, example = false, options = [true, + false, ] } - +background_choice = { optional = true, default = "minecraft", example = "minecraft", options = ["minecraft", "gta", "rocket-league", "motor-gta"], explanation = "Sets the background for the video" } [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." } +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" } 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" } From bd52b4fe8b74c58703aca049ef43a7f50f7389d0 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 6 Jul 2022 22:58:35 -0400 Subject: [PATCH 43/43] some changes to see merge conflicts --- main.py | 5 ++-- video_creation/background.py | 48 +++++++++++++++++++++++++++++++++++ video_creation/final_video.py | 22 +++++++--------- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index c7079d5..568c17e 100755 --- a/main.py +++ b/main.py @@ -8,12 +8,12 @@ from utils.console import print_markdown, print_step from utils import settings # from utils.checker import envUpdate -from video_creation.background import download_background, chop_background_video +from video_creation.background import download_background, chop_background_video, get_background_config 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.2.2" +VERSION = "2.2.8" print( """ ██████╗ ███████╗██████╗ ██████╗ ██╗████████╗ ██╗ ██╗██╗██████╗ ███████╗ ██████╗ ███╗ ███╗ █████╗ ██╗ ██╗███████╗██████╗ @@ -36,6 +36,7 @@ def main(POST_ID=None): reddit_object = get_subreddit_threads(POST_ID) length, number_of_comments = save_text_to_mp3(reddit_object) length = math.ceil(length) + bg_config = get_background_config() download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_background() credit = chop_background_video(length) diff --git a/video_creation/background.py b/video_creation/background.py index 494c7d2..42f6044 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -8,8 +8,56 @@ from moviepy.editor import VideoFileClip from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip from pytube import YouTube +from utils import settings from utils.console import print_step, print_substep +# Supported Background. Can add/remove background video here.... +# - : key -> used as keyword for TOML file. value -> background configuration +# Format (value): +# 1. Youtube URI +# 2. filename +# 3. Citation (owner of the video) +# 4. Position of image clips in the background. See moviepy reference for more information. (https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#moviepy.video.VideoClip.VideoClip.set_position) +background_options = { + "motor-gta": ( # Motor-GTA Racing + "https://www.youtube.com/watch?v=vw5L4xCPy9Q", + "bike-parkour-gta.mp4", + "Achy Gaming", + lambda t: ('center', 480 + t) + ), + "rocket-league": ( # Rocket League + "https://www.youtube.com/watch?v=2X9QGY__0II", + "rocket_league.mp4", + "Orbital Gameplay", + "top" + ), + "minecraft": ( # Minecraft parkour + "https://www.youtube.com/watch?v=n_Dv4JMiwK8", + "parkour.mp4", + "bbswitzer", + "center" + ), + "gta": ( # GTA Stunt Race + "https://www.youtube.com/watch?v=qGa9kWREOnE", + "gta-stunt-race.mp4", + "Achy Gaming", + lambda t: ('center', 480 + t) + ) +} +def get_background_config(): + """Fetch the background/s configuration""" + try: + choice = str(settings.config['settings']['background_choice']).casefold() + except AttributeError: + print_substep("No background selected. Picking random background'") + choice = None + + # Handle default / not supported background using default option. + # Default : pick random from supported background. + if not choice or choice not in background_options: + choice = random.choice(list(background_options.keys())) + + return background_options[choice] def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]: """Generates a random interval of time to be used as the background of the video. diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 42f13b4..c06598e 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -3,7 +3,7 @@ import multiprocessing import os import re from os.path import exists -from typing import Dict +from typing import Dict, Tuple, Any import translators as ts from moviepy.editor import ( @@ -32,7 +32,7 @@ def name_normalize(name: str) -> str: name = re.sub(r'[?\\"%*:|<>]', "", name) name = re.sub(r"( [w,W]\s?\/\s?[o,O,0])", r" without", name) name = re.sub(r"( [w,W]\s?\/)", r" with", name) - name = re.sub(r"([0-9]+)\s?\/\s?([0-9]+)", r"\1 of \2", name) + name = re.sub(r"(\d+)\s?\/\s?(\d+)", r"\1 of \2", name) name = re.sub(r"(\w+)\s?\/\s?(\w+)", r"\1 or \2", name) name = re.sub(r"\/", r"", name) @@ -46,15 +46,13 @@ def name_normalize(name: str) -> str: return name -def make_final_video( - number_of_clips: int, length: int, reddit_obj: dict, background_credit: str -): +def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, background_config: Tuple[str, str, str, Any]): """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 reddit_obj (dict): The reddit object that contains the posts to read. + background_config Tuple[str, str, str, Any]: The background config to use. """ print_step("Creating the final video 🎥") VideoFileClip.reW = lambda clip: clip.resize(width=W) @@ -85,7 +83,6 @@ def make_final_video( 0, ImageClip("assets/temp/png/title.png") .set_duration(audio_clips[0].duration) - .set_position("center") .resize(width=W - 100) .set_opacity(new_opacity), ) @@ -94,7 +91,6 @@ def make_final_video( image_clips.append( ImageClip(f"assets/temp/png/comment_{i}.png") .set_duration(audio_clips[i + 1].duration) - .set_position("center") .resize(width=W - 100) .set_opacity(new_opacity) ) @@ -109,9 +105,9 @@ def make_final_video( # .set_opacity(float(opacity)), # ) # else: - image_concat = concatenate_videoclips(image_clips).set_position( - ("center", "center") - ) + img_clip_pos = background_config[3] + image_concat = concatenate_videoclips( + image_clips).set_position(img_clip_pos) image_concat.audio = audio_composite final = CompositeVideoClip([background_clip, image_concat]) title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"]) @@ -120,7 +116,7 @@ def make_final_video( filename = f"{name_normalize(title)}.mp4" subreddit = settings.config["reddit"]["thread"]["subreddit"] - save_data(filename, title, idx, background_credit) + save_data(filename, title, idx, background_config[2]) if not exists(f"./results/{subreddit}"): print_substep("The results folder didn't exist so I made it") @@ -148,5 +144,5 @@ def make_final_video( print_substep("See result in the results folder!") print_step( - f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_credit}' + f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_config[2]}' )