From dbf68477a6ca144e55155c31ec094f9e5c988e02 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jun 2022 18:09:58 -0400 Subject: [PATCH 1/6] fixed #60 and reformatted project with the black formatter --- main.py | 18 ++-- reddit/subreddit.py | 55 ++++++++---- utils/cleanup.py | 14 ++-- utils/videos.py | 17 ++-- utils/voice.py | 2 +- video_creation/TTSwrapper.py | 106 ++++++++++++++---------- video_creation/background.py | 43 ++++++---- video_creation/final_video.py | 94 ++++++++++++++++----- video_creation/screenshot_downloader.py | 17 ++-- video_creation/voices.py | 20 +++-- 10 files changed, 252 insertions(+), 134 deletions(-) diff --git a/main.py b/main.py index c3d2f83..67cb645 100755 --- a/main.py +++ b/main.py @@ -11,19 +11,20 @@ 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 -banner = ''' +banner = """ ██████╗ ███████╗██████╗ ██████╗ ██╗████████╗ ██╗ ██╗██╗██████╗ ███████╗ ██████╗ ███╗ ███╗ █████╗ ██╗ ██╗███████╗██████╗ ██╔══██╗██╔════╝██╔══██╗██╔══██╗██║╚══██╔══╝ ██║ ██║██║██╔══██╗██╔════╝██╔═══██╗ ████╗ ████║██╔══██╗██║ ██╔╝██╔════╝██╔══██╗ ██████╔╝█████╗ ██║ ██║██║ ██║██║ ██║ ██║ ██║██║██║ ██║█████╗ ██║ ██║ ██╔████╔██║███████║█████╔╝ █████╗ ██████╔╝ ██╔══██╗██╔══╝ ██║ ██║██║ ██║██║ ██║ ╚██╗ ██╔╝██║██║ ██║██╔══╝ ██║ ██║ ██║╚██╔╝██║██╔══██║██╔═██╗ ██╔══╝ ██╔══██╗ ██║ ██║███████╗██████╔╝██████╔╝██║ ██║ ╚████╔╝ ██║██████╔╝███████╗╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██╗███████╗██║ ██║ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ -''' +""" print(banner) load_dotenv() # base code by elebumm print_markdown( - "### Thanks for using this tool! 😊 Feel free to contribute to this project on GitHub! (JasonLovesDoggo/RedditVideoMakerBot). If you have any questions, feel free to reach out to me on Twitter @JasonLovesDoggo or submit a GitHub issue.") + "### Thanks for using this tool! 😊 Feel free to contribute to this project on GitHub! (JasonLovesDoggo/RedditVideoMakerBot). If you have any questions, feel free to reach out to me on Twitter @JasonLovesDoggo or submit a GitHub issue." +) time.sleep(2) @@ -47,15 +48,16 @@ def run_many(times): for x in range(times): x = x + 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}') # correct 1st 2nd 3rd 4th 5th.... + f'on the {x}{("st" if x == 1 else ("nd" if x == 2 else ("rd" if x == 3 else "th")))} iteration of {times}' + ) # correct 1st 2nd 3rd 4th 5th.... main() - Popen('cls' if name == 'nt' else 'clear', shell=True).wait() + Popen("cls" if name == "nt" else "clear", shell=True).wait() -if __name__ == '__main__': +if __name__ == "__main__": try: - if getenv('TIMES_TO_RUN'): - run_many(int(getenv('TIMES_TO_RUN'))) + if getenv("TIMES_TO_RUN"): + run_many(int(getenv("TIMES_TO_RUN"))) else: main() except KeyboardInterrupt: diff --git a/reddit/subreddit.py b/reddit/subreddit.py index e6f5274..e421cd9 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -6,11 +6,11 @@ import praw from utils.console import print_step, print_substep from utils.videos import check_done -TEXT_WHITELIST = set('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890') +TEXT_WHITELIST = set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890") def textify(text): - return ''.join(filter(TEXT_WHITELIST.__contains__, text)) + return "".join(filter(TEXT_WHITELIST.__contains__, text)) def get_subreddit_threads(): @@ -22,29 +22,40 @@ def get_subreddit_threads(): content = {} if getenv("REDDIT_2FA").casefold() == "yes": - 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 = getenv("REDDIT_PASSWORD") passkey = f"{pw}:{code}" else: passkey = getenv("REDDIT_PASSWORD") - reddit = praw.Reddit(client_id=getenv("REDDIT_CLIENT_ID"), client_secret=getenv("REDDIT_CLIENT_SECRET"), - user_agent="Accessing Reddit threads", username=getenv("REDDIT_USERNAME"), - passkey=passkey, check_for_async=False,) + reddit = praw.Reddit( + client_id=getenv("REDDIT_CLIENT_ID"), + client_secret=getenv("REDDIT_CLIENT_SECRET"), + user_agent="Accessing Reddit threads", + username=getenv("REDDIT_USERNAME"), + passkey=passkey, + check_for_async=False, + ) """ Ask user for subreddit input """ if not getenv("SUBREDDIT"): subreddit = reddit.subreddit( - input("What subreddit would you like to pull from? ")) # if the env isnt set, ask user + input("What subreddit would you like to pull from? ") + ) # if the env isnt set, ask user else: - print_substep(f"Using subreddit: r/{getenv('SUBREDDIT')} from environment variable config") + print_substep( + f"Using subreddit: r/{getenv('SUBREDDIT')} from environment variable config" + ) subreddit = reddit.subreddit( - getenv("SUBREDDIT")) # Allows you to specify in .env. Done for automation purposes. + getenv("SUBREDDIT") + ) # Allows you to specify in .env. Done for automation purposes. - if getenv('POST_ID'): - submission = reddit.submission(id=getenv('POST_ID')) + if getenv("POST_ID"): + submission = reddit.submission(id=getenv("POST_ID")) else: threads = subreddit.hot(limit=25) submission = list(threads)[random.randrange(0, 25)] @@ -55,11 +66,15 @@ def get_subreddit_threads(): ratio = submission.upvote_ratio * 100 num_comments = submission.num_comments - print_substep(f"Video will be: {submission.title} :thumbsup:", style='bold green') - print_substep(f"Thread has " + str(upvotes) + " upvotes", style='bold blue') - print_substep(f"Thread has a upvote ratio of " + str(ratio) + "%", style='bold blue') - print_substep(f"Thread has " + str(num_comments) + " comments", style='bold blue') - environ["VIDEO_TITLE"] = str(textify(submission.title)) # todo use global instend of env vars + print_substep(f"Video will be: {submission.title} :thumbsup:", style="bold green") + print_substep(f"Thread has " + str(upvotes) + " upvotes", style="bold blue") + print_substep( + f"Thread has a upvote ratio of " + str(ratio) + "%", style="bold blue" + ) + print_substep(f"Thread has " + str(num_comments) + " comments", style="bold blue") + environ["VIDEO_TITLE"] = str( + textify(submission.title) + ) # todo use global instend of env vars environ["VIDEO_ID"] = str(textify(submission.id)) try: @@ -71,8 +86,12 @@ def get_subreddit_threads(): if not top_level_comment.stickied: if len(top_level_comment.body) <= int(environ["MAX_COMMENT_LENGTH"]): content["comments"].append( - {"comment_body": top_level_comment.body, "comment_url": top_level_comment.permalink, - "comment_id": top_level_comment.id, }) + { + "comment_body": top_level_comment.body, + "comment_url": top_level_comment.permalink, + "comment_id": top_level_comment.id, + } + ) except AttributeError as e: pass print_substep("Received subreddit threads Successfully.", style="bold green") diff --git a/utils/cleanup.py b/utils/cleanup.py index 5c31e05..f3a6c61 100644 --- a/utils/cleanup.py +++ b/utils/cleanup.py @@ -3,20 +3,22 @@ from os.path import exists def cleanup() -> int: - if exists('./assets/temp'): + 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) try: - for file in os.listdir('./assets/temp/mp4'): + for file in os.listdir("./assets/temp/mp4"): count += 1 - os.remove('./assets/temp/mp4/' + file) + os.remove("./assets/temp/mp4/" + file) except FileNotFoundError: pass - for file in os.listdir('./assets/temp/mp3'): + for file in os.listdir("./assets/temp/mp3"): count += 1 - os.remove('./assets/temp/mp3/' + file) + os.remove("./assets/temp/mp3/" + file) return count return 0 diff --git a/utils/videos.py b/utils/videos.py index d22a110..51a2704 100755 --- a/utils/videos.py +++ b/utils/videos.py @@ -4,17 +4,20 @@ from os import getenv from utils.console import print_step -def check_done(redditobj): # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack +def check_done( + redditobj, +): # don't set this to be run anyplace that isn't subreddit.py bc of inspect stack """params: - reddit_object: The Reddit Object you received in askreddit.py""" - with open('./video_creation/data/videos.json', 'r') as done_vids_raw: + reddit_object: The Reddit Object you received in askreddit.py""" + with open("./video_creation/data/videos.json", "r") as done_vids_raw: done_videos = json.load(done_vids_raw) for video in done_videos: - if video['id'] == str(redditobj): - if getenv('POST_ID'): + if video["id"] == str(redditobj): + if getenv("POST_ID"): print_step( - '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 - print_step('Getting new post as the current one has already been done') + print_step("Getting new post as the current one has already been done") return None return redditobj diff --git a/utils/voice.py b/utils/voice.py index 13633da..120ee60 100644 --- a/utils/voice.py +++ b/utils/voice.py @@ -10,7 +10,7 @@ def sanitize_text(text): """ # remove any urls from the text - regex_urls = r'((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*' + regex_urls = r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*" result = re.sub(regex_urls, " ", text) diff --git a/video_creation/TTSwrapper.py b/video_creation/TTSwrapper.py index f5ae5e5..7015d44 100644 --- a/video_creation/TTSwrapper.py +++ b/video_creation/TTSwrapper.py @@ -5,55 +5,55 @@ import os import random import requests from moviepy.editor import AudioFileClip, concatenate_audioclips, CompositeAudioClip -#from profanity_filter import ProfanityFilter -#pf = ProfanityFilter() + +# from profanity_filter import ProfanityFilter +# pf = ProfanityFilter() # Code by @JasonLovesDoggo # https://twitter.com/scanlime/status/1512598559769702406 nonhuman = [ # DISNEY VOICES - 'en_us_ghostface', # Ghost Face - 'en_us_chewbacca', # Chewbacca - 'en_us_c3po', # C3PO - 'en_us_stitch', # Stitch - 'en_us_stormtrooper', # Stormtrooper - 'en_us_rocket', # Rocket - + "en_us_ghostface", # Ghost Face + "en_us_chewbacca", # Chewbacca + "en_us_c3po", # C3PO + "en_us_stitch", # Stitch + "en_us_stormtrooper", # Stormtrooper + "en_us_rocket", # Rocket # ENGLISH VOICES ] -human = ['en_au_001', # English AU - Female - 'en_au_002', # English AU - Male - 'en_uk_001', # English UK - Male 1 - 'en_uk_003', # English UK - Male 2 - 'en_us_001', # English US - Female (Int. 1) - 'en_us_002', # English US - Female (Int. 2) - 'en_us_006', # English US - Male 1 - 'en_us_007', # English US - Male 2 - 'en_us_009', # English US - Male 3 - 'en_us_010'] +human = [ + "en_au_001", # English AU - Female + "en_au_002", # English AU - Male + "en_uk_001", # English UK - Male 1 + "en_uk_003", # English UK - Male 2 + "en_us_001", # English US - Female (Int. 1) + "en_us_002", # English US - Female (Int. 2) + "en_us_006", # English US - Male 1 + "en_us_007", # English US - Male 2 + "en_us_009", # English US - Male 3 + "en_us_010", +] voices = nonhuman + human noneng = [ - 'fr_001', # French - Male 1 - 'fr_002', # French - Male 2 - 'de_001', # German - Female - 'de_002', # German - Male - 'es_002', # Spanish - Male - + "fr_001", # French - Male 1 + "fr_002", # French - Male 2 + "de_001", # German - Female + "de_002", # German - Male + "es_002", # Spanish - Male # AMERICA VOICES - 'es_mx_002', # Spanish MX - Male - 'br_001', # Portuguese BR - Female 1 - 'br_003', # Portuguese BR - Female 2 - 'br_004', # Portuguese BR - Female 3 - 'br_005', # Portuguese BR - Male - + "es_mx_002", # Spanish MX - Male + "br_001", # Portuguese BR - Female 1 + "br_003", # Portuguese BR - Female 2 + "br_004", # Portuguese BR - Female 3 + "br_005", # Portuguese BR - Male # ASIA VOICES - 'id_001', # Indonesian - Female - 'jp_001', # Japanese - Female 1 - 'jp_003', # Japanese - Female 2 - 'jp_005', # Japanese - Female 3 - 'jp_006', # Japanese - Male - 'kr_002', # Korean - Male 1 - 'kr_003', # Korean - Female - 'kr_004', # Korean - Male 2 + "id_001", # Indonesian - Female + "jp_001", # Japanese - Female 1 + "jp_003", # Japanese - Female 2 + "jp_005", # Japanese - Female 3 + "jp_006", # Japanese - Male + "kr_002", # Korean - Male 1 + "kr_003", # Korean - Female + "kr_004", # Korean - Male 2 ] @@ -63,22 +63,36 @@ noneng = [ class TTTTSWrapper: # 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=' - - def tts(self, req_text: str = "TikTok Text To Speech", filename: str = 'title.mp3', random_speaker: bool = False, censer=False): + self.URI_BASE = "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker=" + + def tts( + self, + req_text: str = "TikTok Text To Speech", + filename: str = "title.mp3", + random_speaker: bool = False, + censer=False, + ): req_text = req_text.replace("+", "plus").replace(" ", "+").replace("&", "and") if censer: - #req_text = pf.censor(req_text) + # req_text = pf.censor(req_text) pass - voice = self.randomvoice() if random_speaker else (os.getenv('VOICE') or random.choice(human)) + voice = ( + self.randomvoice() + if random_speaker + else (os.getenv("VOICE") or random.choice(human)) + ) - chunks = [m.group().strip() for m in re.finditer(r' *((.{0,200})(\.|.$))', req_text)] + chunks = [ + m.group().strip() for m in re.finditer(r" *((.{0,200})(\.|.$))", req_text) + ] audio_clips = [] chunkId = 0 for chunk in chunks: - r = requests.post(f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0") + r = requests.post( + f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0" + ) vstr = [r.json()["data"]["v_str"]][0] b64d = base64.b64decode(vstr) diff --git a/video_creation/background.py b/video_creation/background.py index 2d33c2e..943a471 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -14,34 +14,47 @@ def get_start_and_end_times(video_length, length_of_clip): def download_background(): - """Downloads the backgrounds/s video from youtube. - - """ + """Downloads the backgrounds/s video from youtube.""" Path("./assets/backgrounds/").mkdir(parents=True, exist_ok=True) background_options = [ # uri , filename , credit - ("https://www.youtube.com/watch?v=n_Dv4JMiwK8", "parkour.mp4", 'bbswitzer'), - ("https://www.youtube.com/watch?v=2X9QGY__0II", "rocket_league.mp4", 'Orbital Gameplay'), ] + ("https://www.youtube.com/watch?v=n_Dv4JMiwK8", "parkour.mp4", "bbswitzer"), + ( + "https://www.youtube.com/watch?v=2X9QGY__0II", + "rocket_league.mp4", + "Orbital Gameplay", + ), + ] # note: make sure the file name doesn't include a - in it - if len(listdir('./assets/backgrounds')) != len( - background_options): # if there are any background videos not installed - print_step("We need to download the backgnrounds videos. they are fairly large but it's only done once. 😎") + if len(listdir("./assets/backgrounds")) != len( + background_options + ): # if there are any background videos not installed + print_step( + "We need to download the backgnrounds videos. they are fairly large but it's only done once. 😎" + ) print_substep("Downloading the backgrounds videos... please be patient 🙏 ") for uri, filename, credit in background_options: print_substep(f"Downloading {filename} from {uri}") - YouTube(uri).streams.filter(res="1080p").first().download("assets/backgrounds", - filename=f"{credit}-{filename}") + YouTube(uri).streams.filter(res="1080p").first().download( + "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): print_step("Finding a spot in the backgrounds video to chop...✂️") - choice = random.choice(listdir('assets/backgrounds')) - environ["background_credit"] = choice.split('-')[0] + choice = random.choice(listdir("assets/backgrounds")) + environ["background_credit"] = choice.split("-")[0] background = VideoFileClip(f"assets/backgrounds/{choice}") start_time, end_time = get_start_and_end_times(video_length, background.duration) - ffmpeg_extract_subclip(f'assets/backgrounds/{choice}', start_time, end_time, - targetname="assets/temp/background.mp4", ) + ffmpeg_extract_subclip( + f"assets/backgrounds/{choice}", + start_time, + end_time, + targetname="assets/temp/background.mp4", + ) print_substep("Background video chopped successfully! 🎉", style="bold green") diff --git a/video_creation/final_video.py b/video_creation/final_video.py index a41c862..ea237fb 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -3,8 +3,15 @@ import os import time from os.path import exists -from moviepy.editor import VideoFileClip, AudioFileClip, ImageClip, concatenate_videoclips, concatenate_audioclips, \ - CompositeAudioClip, CompositeVideoClip +from moviepy.editor import ( + VideoFileClip, + AudioFileClip, + ImageClip, + concatenate_videoclips, + concatenate_audioclips, + CompositeAudioClip, + CompositeVideoClip, +) from moviepy.video.io import ffmpeg_tools from reddit import subreddit @@ -18,10 +25,13 @@ def make_final_video(number_of_clips, length): 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 = os.getenv("OPACITY") background_clip = ( - VideoFileClip("assets/temp/background.mp4").without_audio().resize(height=H).crop(x1=1166.6, y1=0, x2=2246.6, - y2=1920)) + VideoFileClip("assets/temp/background.mp4") + .without_audio() + .resize(height=H) + .crop(x1=1166.6, y1=0, x2=2246.6, y2=1920) + ) # Gather all audio clips audio_clips = [] for i in range(0, number_of_clips): @@ -33,13 +43,41 @@ def make_final_video(number_of_clips, length): # Gather all images image_clips = [] for i in range(0, number_of_clips): - 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(float(opacity)), ) - image_clips.insert(0, ImageClip(f"assets/temp/png/title.png").set_duration(audio_clips[0].duration).set_position( - "center").resize(width=W - 100).set_opacity(float(opacity)), - ) - image_concat = concatenate_videoclips(image_clips).set_position(("center", "center")) + if opacity is None or opacity >= 1: # opacity not set or is set to one OR MORE + 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), + ) + else: + 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(float(opacity)), + ) + if opacity is None or opacity >= 1: # opacity not set or is set to one OR MORE + image_clips.insert( + 0, + ImageClip(f"assets/temp/png/title.png") + .set_duration(audio_clips[0].duration) + .set_position("center") + .resize(width=W - 100), + ) + else: + image_clips.insert( + 0, + ImageClip(f"assets/temp/png/title.png") + .set_duration(audio_clips[0].duration) + .set_position("center") + .resize(width=W - 100) + .set_opacity(float(opacity)), + ) + image_concat = concatenate_videoclips(image_clips).set_position( + ("center", "center") + ) image_concat.audio = audio_composite final = CompositeVideoClip([background_clip, image_concat]) @@ -50,27 +88,35 @@ def make_final_video(number_of_clips, length): else: return title[0:30] + "..." - filename = f'{get_video_title()}.mp4' + filename = f"{get_video_title()}.mp4" def save_data(): - 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) - 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]: return # video already done but was specified to continue anyway in the .env file - payload = {"id": str(os.getenv("VIDEO_ID")), 'time': str(int(time.time())), - "background_credit": str(os.getenv('background_credit')), - "reddit_title": str(os.getenv('VIDEO_TITLE')), "filename": filename} + payload = { + "id": str(os.getenv("VIDEO_ID")), + "time": str(int(time.time())), + "background_credit": str(os.getenv("background_credit")), + "reddit_title": str(os.getenv("VIDEO_TITLE")), + "filename": filename, + } done_vids.append(payload) raw_vids.seek(0) json.dump(done_vids, raw_vids, ensure_ascii=False, indent=4) save_data() - if not exists('./results'): - print_substep('the results folder didn\'t exist so I made it') + if not exists("./results"): + print_substep("the results folder didn't exist so I made it") os.mkdir("./results") - final.write_videofile("assets/temp/temp.mp4", fps=30, audio_codec="aac", audio_bitrate="192k") - ffmpeg_tools.ffmpeg_extract_subclip("assets/temp/temp.mp4", 0, length, targetname=f"results/{filename}") + final.write_videofile( + "assets/temp/temp.mp4", fps=30, audio_codec="aac", audio_bitrate="192k" + ) + ffmpeg_tools.ffmpeg_extract_subclip( + "assets/temp/temp.mp4", 0, length, targetname=f"results/{filename}" + ) # os.remove("assets/temp/temp.mp4") print_step("Removing temporary files 🗑") @@ -78,4 +124,6 @@ def make_final_video(number_of_clips, length): print_substep(f"Removed {cleanups} temporary files 🗑") print_substep(f"See result in the results folder!") - print_step(f"Reddit title: {os.getenv('VIDEO_TITLE')} \n Background Credit: {os.getenv('background_credit')}") + print_step( + f"Reddit title: {os.getenv('VIDEO_TITLE')} \n Background Credit: {os.getenv('background_credit')}" + ) diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index 6be00fc..fda30f4 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -28,9 +28,9 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num): context = browser.new_context() if getenv("THEME").upper() == "DARK": - cookie_file = open('./video_creation/data/cookie-dark-mode.json') + cookie_file = open("./video_creation/data/cookie-dark-mode.json") else: - cookie_file = open('./video_creation/data/cookie-light-mode.json') + cookie_file = open("./video_creation/data/cookie-light-mode.json") cookies = json.load(cookie_file) context.add_cookies(cookies) # Get the thread screenshot @@ -43,14 +43,19 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num): if getenv("ALLOW_NSFW").casefold() == "false": print_substep("NSFW Post Detected. Skipping...") from main import main + main() print_substep("Post is NSFW. You are spicy... :fire:") page.locator('[data-testid="content-gate"] button').click() - 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" + ) - for idx, comment in track(enumerate(reddit_object["comments"]), "Downloading screenshots..."): + for idx, comment in track( + enumerate(reddit_object["comments"]), "Downloading screenshots..." + ): # Stop if we have reached the screenshot_num if idx >= screenshot_num: @@ -60,5 +65,7 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num): page.locator('[data-testid="content-gate"] button').click() page.goto(f'https://reddit.com{comment["comment_url"]}') - page.locator(f"#t1_{comment['comment_id']}").screenshot(path=f"assets/temp/png/comment_{idx}.png") + page.locator(f"#t1_{comment['comment_id']}").screenshot( + path=f"assets/temp/png/comment_{idx}.png" + ) print_substep("Screenshots downloaded Successfully.", style="bold green") diff --git a/video_creation/voices.py b/video_creation/voices.py index a7822dc..32f468e 100644 --- a/video_creation/voices.py +++ b/video_creation/voices.py @@ -25,7 +25,11 @@ def save_text_to_mp3(reddit_obj): Path("assets/temp/mp3").mkdir(parents=True, exist_ok=True) ttttsw = TTTTSWrapper() # tiktok text to speech wrapper - ttttsw.tts(sanitize_text(reddit_obj["thread_title"]), filename=f"assets/temp/mp3/title.mp3", random_speaker=False) + ttttsw.tts( + sanitize_text(reddit_obj["thread_title"]), + filename=f"assets/temp/mp3/title.mp3", + random_speaker=False, + ) try: length += MP3(f"assets/temp/mp3/title.mp3").info.length except HeaderNotFoundError: # note to self AudioFileClip @@ -36,7 +40,11 @@ def save_text_to_mp3(reddit_obj): if length > VIDEO_LENGTH: break - ttttsw.tts(sanitize_text(comment["comment_body"]), filename=f"assets/temp/mp3/{com}.mp3", random_speaker=False) + ttttsw.tts( + sanitize_text(comment["comment_body"]), + filename=f"assets/temp/mp3/{com}.mp3", + random_speaker=False, + ) try: length += MP3(f"assets/temp/mp3/{com}.mp3").info.length com += 1 @@ -45,9 +53,11 @@ def save_text_to_mp3(reddit_obj): length += sox.file_info.duration(f"assets/temp/mp3/{com}.mp3") com += 1 except (OSError, IOError): - print('would have removed' - f"assets/temp/mp3/{com}.mp3" - f"assets/temp/png/comment_{com}.png") + print( + "would have removed" + f"assets/temp/mp3/{com}.mp3" + f"assets/temp/png/comment_{com}.png" + ) # remove(f"assets/temp/mp3/{com}.mp3") # remove(f"assets/temp/png/comment_{com}.png")# todo might cause odd un-syncing From bff0881b807068772c49f2de5eeab9272ab90c08 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jun 2022 18:12:06 -0400 Subject: [PATCH 2/6] forgot to add int() to Greater than or equal to ops --- 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 ea237fb..2d0afcd 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -43,7 +43,7 @@ def make_final_video(number_of_clips, length): # Gather all images image_clips = [] for i in range(0, number_of_clips): - if opacity is None or opacity >= 1: # opacity not set or is set to one OR MORE + if opacity is None or int(opacity) >= 1: # opacity not set or is set to one OR MORE image_clips.append( ImageClip(f"assets/temp/png/comment_{i}.png") .set_duration(audio_clips[i + 1].duration) @@ -58,7 +58,7 @@ def make_final_video(number_of_clips, length): .resize(width=W - 100) .set_opacity(float(opacity)), ) - if opacity is None or opacity >= 1: # opacity not set or is set to one OR MORE + if opacity is None or int(opacity) >= 1: # opacity not set or is set to one OR MORE image_clips.insert( 0, ImageClip(f"assets/temp/png/title.png") From 174df19be7cfae46301b06068d199aacf8166be6 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jun 2022 18:38:14 -0400 Subject: [PATCH 3/6] fix: improved TTS requests fixes #64 --- .env.template | 2 +- main.py | 2 +- video_creation/TTSwrapper.py | 30 ++++++++++++++++++++---------- video_creation/final_video.py | 4 ++-- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.env.template b/.env.template index a99bcc9..809c6fc 100644 --- a/.env.template +++ b/.env.template @@ -15,7 +15,7 @@ THEME="LIGHT" TIMES_TO_RUN="" MAX_COMMENT_LENGTH="500" # Range is 0 -> 1 recommended around 0.8-0.9 -OPACITY="" +OPACITY="1" # see TTSwrapper.py for all valid options VOICE="en_us_001" # e.g. en_us_002 diff --git a/main.py b/main.py index 67cb645..d86b422 100755 --- a/main.py +++ b/main.py @@ -56,7 +56,7 @@ def run_many(times): if __name__ == "__main__": try: - if getenv("TIMES_TO_RUN"): + if getenv("TIMES_TO_RUN") and isinstance(int(getenv("TIMES_TO_RUN")), int): run_many(int(getenv("TIMES_TO_RUN"))) else: main() diff --git a/video_creation/TTSwrapper.py b/video_creation/TTSwrapper.py index 7015d44..42158ba 100644 --- a/video_creation/TTSwrapper.py +++ b/video_creation/TTSwrapper.py @@ -1,10 +1,11 @@ -import re - import base64 import os import random +import re + import requests from moviepy.editor import AudioFileClip, concatenate_audioclips, CompositeAudioClip +from requests.adapters import HTTPAdapter, Retry # from profanity_filter import ProfanityFilter # pf = ProfanityFilter() @@ -66,11 +67,11 @@ class TTTTSWrapper: # TikTok Text-to-Speech Wrapper self.URI_BASE = "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker=" def tts( - self, - req_text: str = "TikTok Text To Speech", - filename: str = "title.mp3", - random_speaker: bool = False, - censer=False, + self, + req_text: str = "TikTok Text To Speech", + filename: str = "title.mp3", + random_speaker: bool = False, + censer=False, ): req_text = req_text.replace("+", "plus").replace(" ", "+").replace("&", "and") if censer: @@ -90,9 +91,18 @@ class TTTTSWrapper: # TikTok Text-to-Speech Wrapper chunkId = 0 for chunk in chunks: - r = requests.post( - f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0" - ) + try: + r = requests.post( + f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0" + ) + except requests.exceptions.SSLError: + # https://stackoverflow.com/a/47475019/18516611 + session = requests.Session() + retry = Retry(connect=3, backoff_factor=0.5) + adapter = HTTPAdapter(max_retries=retry) + session.mount('http://', adapter) + session.mount('https://', adapter) + r = session.post(f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0") vstr = [r.json()["data"]["v_str"]][0] b64d = base64.b64decode(vstr) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 2d0afcd..42168f7 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -43,7 +43,7 @@ def make_final_video(number_of_clips, length): # Gather all images image_clips = [] for i in range(0, number_of_clips): - if opacity is None or int(opacity) >= 1: # opacity not set or is set to one OR MORE + if opacity is None or float(opacity) >= 1: # opacity not set or is set to one OR MORE image_clips.append( ImageClip(f"assets/temp/png/comment_{i}.png") .set_duration(audio_clips[i + 1].duration) @@ -58,7 +58,7 @@ def make_final_video(number_of_clips, length): .resize(width=W - 100) .set_opacity(float(opacity)), ) - if opacity is None or int(opacity) >= 1: # opacity not set or is set to one OR MORE + if opacity is None or float(opacity) >= 1: # opacity not set or is set to one OR MORE image_clips.insert( 0, ImageClip(f"assets/temp/png/title.png") From 7e3084e88649f53036c29105864964b2630e0feb Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jun 2022 18:38:40 -0400 Subject: [PATCH 4/6] reformatted --- video_creation/TTSwrapper.py | 18 ++++++++++-------- video_creation/final_video.py | 8 ++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/video_creation/TTSwrapper.py b/video_creation/TTSwrapper.py index 42158ba..d8fa2ec 100644 --- a/video_creation/TTSwrapper.py +++ b/video_creation/TTSwrapper.py @@ -67,11 +67,11 @@ class TTTTSWrapper: # TikTok Text-to-Speech Wrapper self.URI_BASE = "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker=" def tts( - self, - req_text: str = "TikTok Text To Speech", - filename: str = "title.mp3", - random_speaker: bool = False, - censer=False, + self, + req_text: str = "TikTok Text To Speech", + filename: str = "title.mp3", + random_speaker: bool = False, + censer=False, ): req_text = req_text.replace("+", "plus").replace(" ", "+").replace("&", "and") if censer: @@ -100,9 +100,11 @@ class TTTTSWrapper: # TikTok Text-to-Speech Wrapper session = requests.Session() retry = Retry(connect=3, backoff_factor=0.5) adapter = HTTPAdapter(max_retries=retry) - session.mount('http://', adapter) - session.mount('https://', adapter) - r = session.post(f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0") + session.mount("http://", adapter) + session.mount("https://", adapter) + r = session.post( + f"{self.URI_BASE}{voice}&req_text={chunk}&speaker_map_type=0" + ) vstr = [r.json()["data"]["v_str"]][0] b64d = base64.b64decode(vstr) diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 42168f7..0df7a1f 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -43,7 +43,9 @@ def make_final_video(number_of_clips, length): # Gather all images image_clips = [] for i in range(0, number_of_clips): - if opacity is None or float(opacity) >= 1: # opacity not set or is set to one OR MORE + if ( + opacity is None or float(opacity) >= 1 + ): # opacity not set or is set to one OR MORE image_clips.append( ImageClip(f"assets/temp/png/comment_{i}.png") .set_duration(audio_clips[i + 1].duration) @@ -58,7 +60,9 @@ def make_final_video(number_of_clips, length): .resize(width=W - 100) .set_opacity(float(opacity)), ) - if opacity is None or float(opacity) >= 1: # opacity not set or is set to one OR MORE + if ( + opacity is None or float(opacity) >= 1 + ): # opacity not set or is set to one OR MORE image_clips.insert( 0, ImageClip(f"assets/temp/png/title.png") From 0954906e7992e6c8e3330292be611db931f072b7 Mon Sep 17 00:00:00 2001 From: Jason <66544866+JasonLovesDoggo@users.noreply.github.com> Date: Wed, 8 Jun 2022 11:20:20 -0400 Subject: [PATCH 5/6] improved the submission "getting" system fixes #65 --- reddit/subreddit.py | 10 ++++++---- utils/subreddit.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 utils/subreddit.py diff --git a/reddit/subreddit.py b/reddit/subreddit.py index e421cd9..cc5bc02 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -5,6 +5,7 @@ import praw from utils.console import print_step, print_substep from utils.videos import check_done +from utils.subreddit import get_hottest_undone TEXT_WHITELIST = set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890") @@ -18,7 +19,7 @@ def get_subreddit_threads(): Returns a list of threads from the AskReddit subreddit. """ global submission - print_step("Getting subreddit threads...") + print_step("Logging into Reddit.") content = {} if getenv("REDDIT_2FA").casefold() == "yes": @@ -42,7 +43,8 @@ def get_subreddit_threads(): """ Ask user for subreddit input """ - if not getenv("SUBREDDIT"): + print_step("Getting subreddit threads...") + if not getenv("SUBREDDIT"): # note to self. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython") subreddit = reddit.subreddit( input("What subreddit would you like to pull from? ") ) # if the env isnt set, ask user @@ -58,8 +60,8 @@ def get_subreddit_threads(): submission = reddit.submission(id=getenv("POST_ID")) else: threads = subreddit.hot(limit=25) - submission = list(threads)[random.randrange(0, 25)] - submission = check_done(submission) + submission = get_hottest_undone(threads) + submission = check_done(submission) # double checking if submission is None: return get_subreddit_threads() # submission already done. rerun upvotes = submission.score diff --git a/utils/subreddit.py b/utils/subreddit.py new file mode 100644 index 0000000..f62559f --- /dev/null +++ b/utils/subreddit.py @@ -0,0 +1,19 @@ +from typings import List +def get_hottest_undone(submissions: List): + """ + recursively checks if the top submission in the list was already done. + """ + with open("./video_creation/data/videos.json", "r") as done_vids_raw: + done_videos = json.load(done_vids_raw) + for submission in submissions: + if already_done(done_videos, submission): + continue + return submission + return get_subreddit_undone(subreddit.top(time_filter="hour")) # all of the videos in hot have already been done + +def already_done(done_videos: list, submission) + + for video in done_videos: + if video["id"] == str(submission): + return True + return False From a923b2c9e765fa996099f6d6df0eba73419fee08 Mon Sep 17 00:00:00 2001 From: Jason <66544866+JasonLovesDoggo@users.noreply.github.com> Date: Wed, 8 Jun 2022 11:27:30 -0400 Subject: [PATCH 6/6] left char --- utils/subreddit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/subreddit.py b/utils/subreddit.py index f62559f..7fd7117 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -11,7 +11,7 @@ def get_hottest_undone(submissions: List): return submission return get_subreddit_undone(subreddit.top(time_filter="hour")) # all of the videos in hot have already been done -def already_done(done_videos: list, submission) +def already_done(done_videos: list, submission): for video in done_videos: if video["id"] == str(submission):