pull/760/head
HallowedDust5 3 years ago
parent 34ce61e1ca
commit b8516577b9
No known key found for this signature in database
GPG Key ID: AAAAF940F1C8C004

@ -69,7 +69,8 @@ class TTSEngine:
else: else:
self.split_post(comment["comment_body"], idx) self.split_post(comment["comment_body"], idx)
print_substep("Saved Text to MP3 files successfully.", style="bold green") print_substep("Saved Text to MP3 files successfully.",
style="bold green")
return self.length, idx return self.length, idx
def split_post(self, text: str, idx: int) -> str: def split_post(self, text: str, idx: int) -> str:
@ -85,7 +86,8 @@ class TTSEngine:
for idy, text_cut in enumerate(split_text): for idy, text_cut in enumerate(split_text):
# print(f"{idx}-{idy}: {text_cut}\n") # print(f"{idx}-{idy}: {text_cut}\n")
self.call_tts(f"{idx}-{idy}.part", text_cut) self.call_tts(f"{idx}-{idy}.part", text_cut)
split_files.append(AudioFileClip(f"{self.path}/{idx}-{idy}.part.mp3")) split_files.append(AudioFileClip(
f"{self.path}/{idx}-{idy}.part.mp3"))
CompositeAudioClip([concatenate_audioclips(split_files)]).write_audiofile( CompositeAudioClip([concatenate_audioclips(split_files)]).write_audiofile(
f"{self.path}/{idx}.mp3", fps=44100, verbose=False, logger=None f"{self.path}/{idx}.mp3", fps=44100, verbose=False, logger=None
) )

@ -12,7 +12,7 @@ def program_options():
""" """
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog="RedditVideoMakerBot", # can be renamed, just a base prog="RedditVideoMakerBot", # can be renamed, just a base
usage="RedditVideoMakerBot [OPTIONS]", usage="RedditVideoMakerBot [OPTIONS]",
description=description description=description
) )
@ -21,7 +21,7 @@ def program_options():
help="Create a video (uses the defaults).", help="Create a video (uses the defaults).",
action="store_true" action="store_true"
) )
parser.add_argument( # only accepts the name of subreddit, not links. parser.add_argument( # only accepts the name of subreddit, not links.
"-s", "--subreddit", "-s", "--subreddit",
help="Specify a subreddit.", help="Specify a subreddit.",
action="store" action="store"
@ -65,7 +65,8 @@ def program_options():
args.number, args.number,
) )
if not create: if not create:
try_again = input("Something went wrong! Try again? [y/N] > ").strip() try_again = input(
"Something went wrong! Try again? [y/N] > ").strip()
if try_again in ["y", "Y"]: if try_again in ["y", "Y"]:
continue continue

@ -32,29 +32,27 @@ print_markdown(
def main( def main(
subreddit_=None, subreddit_=None,
background=None, background=None,
filename=None, filename=None,
thread_link_=None, thread_link_=None,
number_of_comments=None number_of_comments=None
): ):
if check_env() is not True: if check_env() is not True:
exit() exit()
load_dotenv() load_dotenv()
cleanup() cleanup()
reddit_object = get_subreddit_threads( reddit_object = get_subreddit_threads(
subreddit_, subreddit_,
thread_link_, thread_link_,
number_of_comments number_of_comments
) )
length, number_of_comments = save_text_to_mp3(reddit_object) length, number_of_comments = save_text_to_mp3(reddit_object)
download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_screenshots_of_reddit_posts(reddit_object, number_of_comments)
download_background(background) download_background(background)
chop_background_video(length) chop_background_video(length)
make_final_video(number_of_comments, length,filename) make_final_video(number_of_comments, length, filename)
def run_many(times): def run_many(times):

@ -1,3 +1,10 @@
from dotenv import load_dotenv
from prawcore.exceptions import (
OAuthException,
ResponseException,
RequestException,
BadRequest
)
import random import random
import os import os
import re import re
@ -10,7 +17,8 @@ from utils.subreddit import get_subreddit_undone
from utils.videos import check_done from utils.videos import check_done
from praw.models import MoreComments from praw.models import MoreComments
TEXT_WHITELIST = set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890") TEXT_WHITELIST = set(
"abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890")
def textify(text): def textify(text):
@ -24,17 +32,6 @@ def try_env(param, backup):
return backup return backup
from prawcore.exceptions import (
OAuthException,
ResponseException,
RequestException,
BadRequest
)
from dotenv import load_dotenv
from utils.console import print_step, print_substep
def get_subreddit_threads(subreddit_, thread_link_, number_of_comments): def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
""" """
Takes subreddit_ as parameter which defaults to None, but in this Takes subreddit_ as parameter which defaults to None, but in this
@ -67,11 +64,11 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
password=passkey.strip(), password=passkey.strip(),
) )
except ( except (
OAuthException, OAuthException,
ResponseException, ResponseException,
RequestException, RequestException,
BadRequest BadRequest
): ):
print_substep( print_substep(
"[bold red]There is something wrong with the .env file, kindly check:[/bold red]\n" "[bold red]There is something wrong with the .env file, kindly check:[/bold red]\n"
+ "1. ClientID\n" + "1. ClientID\n"
@ -80,7 +77,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
+ "4. Check if the type of Reddit app created is script (personal use script)." + "4. Check if the type of Reddit app created is script (personal use script)."
) )
# If the user specifies that he doesnt want a random thread, or if # If the user specifies that he doesnt want a random thread, or if
# he doesn't insert the "RANDOM_THREAD" variable at all, ask the thread link # he doesn't insert the "RANDOM_THREAD" variable at all, ask the thread link
while True: while True:
@ -136,7 +132,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
+ f"And has a {num_comments}[/blue].\n" + f"And has a {num_comments}[/blue].\n"
) )
try: try:
content["thread_url"] = submission.url content["thread_url"] = submission.url
content["thread_title"] = submission.title content["thread_title"] = submission.title
@ -160,7 +155,8 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
except AttributeError: except AttributeError:
pass pass
print_substep("AskReddit threads retrieved successfully.", style="bold green") print_substep("AskReddit threads retrieved successfully.",
style="bold green")
content["thread_url"] = f"https://reddit.com{submission.permalink}" content["thread_url"] = f"https://reddit.com{submission.permalink}"
content["thread_title"] = submission.title content["thread_title"] = submission.title
@ -180,5 +176,6 @@ def get_subreddit_threads(subreddit_, thread_link_, number_of_comments):
"comment_id": top_level_comment.id, "comment_id": top_level_comment.id,
} }
) )
print_substep("Received subreddit threads Successfully.", style="bold green") print_substep("Received subreddit threads Successfully.",
style="bold green")
return content return content

@ -108,7 +108,8 @@ user = handle_input(
20, 20,
"A username HAS to be between 3 and 20 characters", "A username HAS to be between 3 and 20 characters",
) )
passw = handle_input("Password > ", False, ".*", "", 8, None, "Password too short") passw = handle_input("Password > ", False, ".*", "",
8, None, "Password too short")
twofactor = handle_input( twofactor = handle_input(
"2fa Enabled? (yes/no) > ", "2fa Enabled? (yes/no) > ",
False, False,

@ -15,9 +15,10 @@ def check_env() -> bool:
Returns: Returns:
bool: Whether or not everything was put in properly bool: Whether or not everything was put in properly
""" """
if not os.path.exists(".env.template"): if not os.path.exists(".env.template"):
console.print("[red]Couldn't find .env.template. Unable to check variables.") console.print(
"[red]Couldn't find .env.template. Unable to check variables.")
return True return True
if not os.path.exists(".env"): if not os.path.exists(".env"):
console.print("[red]Couldn't find the .env file, creating one now.") console.print("[red]Couldn't find the .env file, creating one now.")
@ -38,14 +39,17 @@ def check_env() -> bool:
if line.startswith("#") is not True and "=" in line and var_optional is not True: if line.startswith("#") is not True and "=" in line and var_optional is not True:
req_envs.append(line.split("=")[0]) req_envs.append(line.split("=")[0])
if "#" in line: if "#" in line:
examples[line.split("=")[0]] = "#".join(line.split("#")[1:]).strip() examples[line.split("=")[0]] = "#".join(
line.split("#")[1:]).strip()
elif "#OPTIONAL" in line: elif "#OPTIONAL" in line:
var_optional = True var_optional = True
elif line.startswith("#MATCH_REGEX "): elif line.startswith("#MATCH_REGEX "):
matching[req_envs[-1]] = line.removeprefix("#MATCH_REGEX ")[:-1] matching[req_envs[-1]
] = line.removeprefix("#MATCH_REGEX ")[:-1]
var_optional = False var_optional = False
elif line.startswith("#OOB_ERROR "): elif line.startswith("#OOB_ERROR "):
oob_errors[req_envs[-1]] = line.removeprefix("#OOB_ERROR ")[:-1] oob_errors[req_envs[-1]
] = line.removeprefix("#OOB_ERROR ")[:-1]
var_optional = False var_optional = False
elif line.startswith("#RANGE "): elif line.startswith("#RANGE "):
bounds[req_envs[-1]] = tuple( bounds[req_envs[-1]] = tuple(
@ -56,10 +60,12 @@ def check_env() -> bool:
) )
var_optional = False var_optional = False
elif line.startswith("#MATCH_TYPE "): elif line.startswith("#MATCH_TYPE "):
types[req_envs[-1]] = eval(line.removeprefix("#MATCH_TYPE ")[:-1].split()[0]) types[req_envs[-1]
] = eval(line.removeprefix("#MATCH_TYPE ")[:-1].split()[0])
var_optional = False var_optional = False
elif line.startswith("#EXPLANATION "): elif line.startswith("#EXPLANATION "):
explanations[req_envs[-1]] = line.removeprefix("#EXPLANATION ")[:-1] explanations[req_envs[-1]
] = line.removeprefix("#EXPLANATION ")[:-1]
var_optional = False var_optional = False
else: else:
var_optional = False var_optional = False
@ -99,17 +105,22 @@ def check_env() -> bool:
title_justify="left", title_justify="left",
title_style="#C0CAF5 bold", title_style="#C0CAF5 bold",
) )
table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) table.add_column("Variable", justify="left",
table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) style="#7AA2F7 bold", no_wrap=True)
table.add_column("Example", justify="center", style="#F7768E", 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("Min", justify="right", style="#F7768E", no_wrap=True)
table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True)
for env in missing: for env in missing:
table.add_row( table.add_row(
env, env,
explanations[env] if env in explanations.keys() else "No explanation given", explanations[env] if env in explanations.keys(
) else "No explanation given",
examples[env] if env in examples.keys() else "", examples[env] if env in examples.keys() else "",
str(bounds[env][0]) if env in bounds.keys() and bounds[env][1] is not None else "", str(bounds[env][0]) if env in bounds.keys(
) and bounds[env][1] is not None else "",
str(bounds[env][1]) str(bounds[env][1])
if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None
else "", else "",
@ -127,20 +138,26 @@ def check_env() -> bool:
title_justify="left", title_justify="left",
title_style="#C0CAF5 bold", title_style="#C0CAF5 bold",
) )
table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) table.add_column("Variable", justify="left",
table.add_column("Current value", justify="left", style="#F7768E", no_wrap=False) style="#7AA2F7 bold", no_wrap=True)
table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) table.add_column("Current value", justify="left",
table.add_column("Example", justify="center", style="#F7768E", no_wrap=True) 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("Min", justify="right", style="#F7768E", no_wrap=True)
table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True)
for env in incorrect: for env in incorrect:
table.add_row( table.add_row(
env, env,
os.getenv(env), os.getenv(env),
explanations[env] if env in explanations.keys() else "No explanation given", explanations[env] if env in explanations.keys(
) else "No explanation given",
str(types[env].__name__) if env in types.keys() else "str", str(types[env].__name__) if env in types.keys() else "str",
str(bounds[env][0]) if env in bounds.keys() else "None", str(bounds[env][0]) if env in bounds.keys() else "None",
str(bounds[env][1]) if env in bounds.keys() and len(bounds[env]) > 1 else "None", str(bounds[env][1]) if env in bounds.keys() and len(
bounds[env]) > 1 else "None",
) )
missing.add(env) missing.add(env)
console.print(table) console.print(table)
@ -157,11 +174,13 @@ def check_env() -> bool:
with open(".env", "r+") as env_file: with open(".env", "r+") as env_file:
lines = [] lines = []
for line in env_file.readlines(): for line in env_file.readlines():
line.split("=")[0].strip() not in incorrect and lines.append(line) line.split("=")[0].strip(
) not in incorrect and lines.append(line)
env_file.seek(0) env_file.seek(0)
env_file.write("\n".join(lines)) env_file.write("\n".join(lines))
env_file.truncate() env_file.truncate()
console.print("[green]Successfully removed incorrectly set variables from .env") console.print(
"[green]Successfully removed incorrectly set variables from .env")
with open(".env", "a") as env_file: with open(".env", "a") as env_file:
for env in missing: for env in missing:
env_file.write( env_file.write(
@ -177,11 +196,14 @@ def check_env() -> bool:
if env in explanations.keys() if env in explanations.keys()
else "Incorrect input. Try again.", else "Incorrect input. Try again.",
bounds[env][0] if env in bounds.keys() else None, 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, bounds[env][1] if env in bounds.keys() and len(
oob_errors[env] if env in oob_errors.keys() else "Input too long/short.", bounds[env]) > 1 else None,
oob_errors[env] if env in oob_errors.keys(
) else "Input too long/short.",
extra_info="[#C0CAF5 bold]⮶ " extra_info="[#C0CAF5 bold]⮶ "
+ ( + (
explanations[env] if env in explanations.keys() else "No info available" explanations[env] if env in explanations.keys(
) else "No info available"
), ),
) )
) )

@ -7,10 +7,11 @@ def cleanup() -> int:
Returns: Returns:
int: How many files were deleted int: How many files were deleted
""" """
if exists("./assets/temp"): if exists("./assets/temp"):
count = 0 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) count += len(files)
for f in files: for f in files:
os.remove(f) os.remove(f)

@ -13,7 +13,7 @@ def get_subreddit_undone(submissions: list, subreddit):
Returns: Returns:
Any: The submission that has not been done Any: The submission that has not been done
""" """
""" """
recursively checks if the top submission in the list was already done. recursively checks if the top submission in the list was already done.
""" """
@ -28,7 +28,8 @@ def get_subreddit_undone(submissions: list, subreddit):
print_substep("NSFW Post Detected. Skipping...") print_substep("NSFW Post Detected. Skipping...")
continue continue
except AttributeError: except AttributeError:
print_substep("NSFW settings not defined. Skipping NSFW post...") print_substep(
"NSFW settings not defined. Skipping NSFW post...")
return submission return submission
print("all submissions have been done going by top submission order") print("all submissions have been done going by top submission order")
return get_subreddit_undone( return get_subreddit_undone(
@ -36,7 +37,7 @@ def get_subreddit_undone(submissions: list, subreddit):
) # all of the videos in hot have already been done ) # all of the videos in hot have already been done
def already_done(done_videos: list, submission)->bool: def already_done(done_videos: list, submission) -> bool:
"""Checks to see if the given submission is in the list of videos """Checks to see if the given submission is in the list of videos
Args: Args:
@ -45,7 +46,7 @@ def already_done(done_videos: list, submission)->bool:
Returns: Returns:
Boolean: Whether the video was found in the list Boolean: Whether the video was found in the list
""" """
for video in done_videos: for video in done_videos:
if video["id"] == str(submission): if video["id"] == str(submission):

@ -5,8 +5,8 @@ from utils.console import print_step
def check_done( def check_done(
redditobj:dict[str], redditobj: dict[str],
)->dict[str]|None: # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack ) -> dict[str] | None: # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack
"""Checks if the chosen post has already been generated """Checks if the chosen post has already been generated
Args: Args:
@ -25,6 +25,7 @@ def check_done(
"You already have done this video but since it was declared specifically in the .env file the program will continue" "You already have done this video but since it was declared specifically in the .env file the program will continue"
) )
return redditobj return redditobj
print_step("Getting new post as the current one has already been done") print_step(
"Getting new post as the current one has already been done")
return None return None
return redditobj return redditobj

@ -28,13 +28,13 @@ console = Console()
W, H = 1080, 1920 W, H = 1080, 1920
def make_final_video(number_of_clips:int, length:int,final_vid_path:str): def make_final_video(number_of_clips: int, length: int, final_vid_path: str):
"""Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp """Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp
Args: Args:
number_of_clips (int): Index to end at when going through the screenshots number_of_clips (int): Index to end at when going through the screenshots
length (int): Length of the video length (int): Length of the video
""" """
print_step("Creating the final video 🎥") print_step("Creating the final video 🎥")
VideoFileClip.reW = lambda clip: clip.resize(width=W) VideoFileClip.reW = lambda clip: clip.resize(width=W)
VideoFileClip.reH = lambda clip: clip.resize(width=H) VideoFileClip.reH = lambda clip: clip.resize(width=H)
@ -49,10 +49,10 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str):
try: try:
opacity = float(os.getenv("OPACITY")) opacity = float(os.getenv("OPACITY"))
except ( except (
ValueError, ValueError,
FloatingPointError, FloatingPointError,
TypeError TypeError
): ):
print_substep( print_substep(
"Please ensure that OPACITY is between 0 and 1 in .env file", style_="bold red" "Please ensure that OPACITY is between 0 and 1 in .env file", style_="bold red"
) )
@ -66,16 +66,15 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str):
try: try:
audio_clips.insert(1, AudioFileClip("assets/mp3/posttext.mp3")) audio_clips.insert(1, AudioFileClip("assets/mp3/posttext.mp3"))
except ( except (
OSError, OSError,
FileNotFoundError, FileNotFoundError,
): ):
print_substep("An error occured! Aborting.", style_="bold red") print_substep("An error occured! Aborting.", style_="bold red")
raise SystemExit() raise SystemExit()
else: else:
audio_concat = concatenate_audioclips(audio_clips) audio_concat = concatenate_audioclips(audio_clips)
audio_composite = CompositeAudioClip([audio_concat]) audio_composite = CompositeAudioClip([audio_concat])
# Get sum of all clip lengths # Get sum of all clip lengths
total_length = sum([clip.duration for clip in audio_clips]) total_length = sum([clip.duration for clip in audio_clips])
# round total_length to an integer # round total_length to an integer
@ -142,13 +141,12 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str):
final = CompositeVideoClip([background_clip, image_concat]) final = CompositeVideoClip([background_clip, image_concat])
if final_vid_path is None: if final_vid_path is None:
final_vid_path = re.sub( final_vid_path = re.sub(
"[?\"%*:|<>]/", "", (f"assets/{subreddit.submission.title}.mp4") "[?\"%*:|<>]/", "", (f"assets/{subreddit.submission.title}.mp4")
) )
final.write_videofile(final_vid_path, fps=30, audio_codec="aac", audio_bitrate="192k") final.write_videofile(final_vid_path, fps=30,
audio_codec="aac", audio_bitrate="192k")
save_data(final_vid_path) save_data(final_vid_path)
@ -177,12 +175,13 @@ def make_final_video(number_of_clips:int, length:int,final_vid_path:str):
f"Reddit title: {os.getenv('VIDEO_TITLE')} \n Background Credit: {os.getenv('background_credit')}" f"Reddit title: {os.getenv('VIDEO_TITLE')} \n Background Credit: {os.getenv('background_credit')}"
) )
def save_data(filename:str):
def save_data(filename: str):
"""Saves the videos that have already been generated to a JSON file in video_creation/data/videos.json """Saves the videos that have already been generated to a JSON file in video_creation/data/videos.json
Args: Args:
filename (str): The finished video title name filename (str): The finished video title name
""" """
with open("./video_creation/data/videos.json", "r+") as raw_vids: with open("./video_creation/data/videos.json", "r+") as raw_vids:
done_vids = json.load(raw_vids) done_vids = json.load(raw_vids)
if str(subreddit.submission.id) in [video["id"] for video in done_vids]: if str(subreddit.submission.id) in [video["id"] for video in done_vids]:
@ -198,12 +197,13 @@ def save_data(filename:str):
raw_vids.seek(0) raw_vids.seek(0)
json.dump(done_vids, raw_vids, ensure_ascii=False, indent=4) json.dump(done_vids, raw_vids, ensure_ascii=False, indent=4)
def get_video_title() -> str: def get_video_title() -> str:
"""Gets video title from env variable or gives it the name "final_video" """Gets video title from env variable or gives it the name "final_video"
Returns: Returns:
str: Video title str: Video title
""" """
title = os.getenv("VIDEO_TITLE") or "final_video" title = os.getenv("VIDEO_TITLE") or "final_video"
if len(title) <= 35: if len(title) <= 35:
return title return title

@ -27,7 +27,7 @@ TTSProviders = {
VIDEO_LENGTH: int = 40 # secs VIDEO_LENGTH: int = 40 # secs
def save_text_to_mp3(reddit_obj:dict[str])->tuple[int,int]: def save_text_to_mp3(reddit_obj: dict[str]) -> tuple[int, int]:
"""Saves text to MP3 files. Goes through the reddit_obj and generates the title MP3 file and a certain number of comments until the total amount of time exceeds VIDEO_LENGTH seconds. """Saves text to MP3 files. Goes through the reddit_obj and generates the title MP3 file and a certain number of comments until the total amount of time exceeds VIDEO_LENGTH seconds.
Args: Args:
@ -36,7 +36,7 @@ def save_text_to_mp3(reddit_obj:dict[str])->tuple[int,int]:
Returns: Returns:
tuple[int,int]: (total length of the audio, the number of comments audio was generated for) tuple[int,int]: (total length of the audio, the number of comments audio was generated for)
""" """
env = os.getenv("TTSCHOICE", "") env = os.getenv("TTSCHOICE", "")
if env.casefold() in map(lambda _: _.casefold(), TTSProviders): if env.casefold() in map(lambda _: _.casefold(), TTSProviders):
text_to_mp3 = TTSEngine( text_to_mp3 = TTSEngine(

Loading…
Cancel
Save