From 1424b3edd5ecc2c4047664b9b002d1535947a0ec Mon Sep 17 00:00:00 2001 From: Syed Aman Raza <109358640+electro199@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:42:07 +0500 Subject: [PATCH] Added the stroy mode with two method and add min comment lenght --- TTS/engine_wrapper.py | 59 ++++++++++---------- reddit/subreddit.py | 56 +++++++++++-------- utils/.config.template.toml | 5 +- utils/imagenarator.py | 41 ++++++++++++++ utils/posttextparser.py | 46 +++++++++++++++ utils/subreddit.py | 3 + video_creation/final_video.py | 74 ++++++++++++++++--------- video_creation/screenshot_downloader.py | 22 +++++--- 8 files changed, 221 insertions(+), 85 deletions(-) create mode 100644 utils/imagenarator.py create mode 100644 utils/posttextparser.py diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index 12668df..559abf5 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -13,8 +13,7 @@ from utils.console import print_step, print_substep from utils.voice import sanitize_text from utils import settings -DEFAULT_MAX_LENGTH: int = 50 # video length variable - +DEFAULT_MAX_LENGTH: int = 40 # video length variable class TTSEngine: @@ -40,6 +39,7 @@ class TTSEngine: ): self.tts_module = tts_module() self.reddit_object = reddit_object + self.redditid = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"]) self.path = path + self.redditid + "/mp3" self.max_length = max_length @@ -48,39 +48,42 @@ class TTSEngine: def run(self) -> Tuple[int, int]: - Path(self.path).mkdir(parents=True, exist_ok=True) - - # This file needs to be removed in case this post does not use post text, so that it won't appear in the final video - try: - Path(f"{self.path}/posttext.mp3").unlink() - except OSError: - pass - + Path(self.path).mkdir(parents=True, exist_ok=True) print_step("Saving Text to MP3 files...") - + self.call_tts("title", process_text(self.reddit_object["thread_title"])) - processed_text = process_text(self.reddit_object["thread_post"]) - if processed_text != "" and settings.config["settings"]["storymode"] == True: - self.call_tts("posttext", processed_text) - + # processed_text = ##self.reddit_object["thread_post"] != "" idx = None - 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: - self.length -= self.last_clip_length - idx -= 1 - break - if ( - len(comment["comment_body"]) > self.tts_module.max_chars - ): # Split the comment if it is too long - self.split_post(comment["comment_body"], idx) # Split the comment - else: # If the comment is not too long, just call the tts engine - self.call_tts(f"{idx}", process_text(comment["comment_body"])) + if settings.config["settings"]["storymode"] : + if settings.config["settings"]["storymodemethode"] == 0: + if (len(self.reddit_object["thread_post"]) > self.tts_module.max_chars): + self.split_post(self.reddit_object["thread_post"], "postaudio") + else : + self.call_tts("postaudio",process_text(self.reddit_object["thread_post"]) ) + elif settings.config["settings"]["storymodemethode"] == 1: + + for idx,text in enumerate(self.reddit_object["thread_post"]): + self.call_tts(f"posttext-{idx}",process_text(text) ) + + else : + + 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 and idx > 1: + self.length -= self.last_clip_length + idx -= 1 + break + if ( + len(comment["comment_body"]) > self.tts_module.max_chars + ): # Split the comment if it is too long + self.split_post(comment["comment_body"], idx) # Split the comment + else: # If the comment is not too long, just call the tts engine + self.call_tts(f"{idx}", process_text(comment["comment_body"])) print_substep("Saved Text to MP3 files successfully.", style="bold green") return self.length, idx - def split_post(self, text: str, idx: int): + def split_post(self, text: str, idx): split_files = [] split_text = [ x.group().strip() diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 11b93af..bab798b 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -10,6 +10,7 @@ from utils.console import print_step, print_substep from utils.subreddit import get_subreddit_undone from utils.videos import check_done from utils.voice import sanitize_text +from utils.posttextparser import posttextparser def get_subreddit_threads(POST_ID: str): @@ -70,6 +71,7 @@ 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(str(settings.config["reddit"]["thread"]["post_id"]).split("+")) == 1 @@ -92,32 +94,40 @@ def get_subreddit_threads(POST_ID: str): content["thread_url"] = f"https://reddit.com{submission.permalink}" content["thread_title"] = submission.title - content["thread_post"] = submission.selftext content["thread_id"] = submission.id content["comments"] = [] - - for top_level_comment in submission.comments: - if isinstance(top_level_comment, MoreComments): - continue - if top_level_comment.body in ["[removed]", "[deleted]"]: - continue # # see https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/78 - if not top_level_comment.stickied: - sanitised = sanitize_text(top_level_comment.body) - if not sanitised or sanitised == " ": + if settings.config["settings"]["storymode"]: + if settings.config["settings"]["storymodemethode"] == 1: + content["thread_post"] = posttextparser(submission.selftext) + else: + content["thread_post"] =submission.selftext + else: + for top_level_comment in submission.comments: + if isinstance(top_level_comment, MoreComments): continue - if len(top_level_comment.body) <= int( + if top_level_comment.body in ["[removed]", "[deleted]"]: + continue # # see https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/78 + if not top_level_comment.stickied: + sanitised = sanitize_text(top_level_comment.body) + if not sanitised or sanitised == " ": + continue + if len(top_level_comment.body) <= int( settings.config["reddit"]["thread"]["max_comment_length"] - ): - if ( - top_level_comment.author is not None - and sanitize_text(top_level_comment.body) is not None - ): # if errors occur with this change to if not. - content["comments"].append( - { - "comment_body": top_level_comment.body, - "comment_url": top_level_comment.permalink, - "comment_id": top_level_comment.id, - } - ) + ): + if len(top_level_comment.body)>= int( + settings.config["reddit"]["thread"]["min_comment_length"] + ): + + if ( + top_level_comment.author is not None + and sanitize_text(top_level_comment.body) is not None + ): # if errors occur with this change to if not. + content["comments"].append( + { + "comment_body": top_level_comment.body, + "comment_url": top_level_comment.permalink, + "comment_id": top_level_comment.id, + } + ) print_substep("Received subreddit threads Successfully.", style="bold green") return content diff --git a/utils/.config.template.toml b/utils/.config.template.toml index adbaed0..2e77c7e 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -11,8 +11,9 @@ random = { optional = true, options = [true, false,], default = false, type = "b subreddit = { optional = false, regex = "[_0-9a-zA-Z]+$", nmin = 3, explanation = "What subreddit to pull posts from, the name of the sub, not the URL. You can have multiple subreddits, add an + with no spaces.", example = "AskReddit+Redditdev", oob_error = "A subreddit name HAS to be between 3 and 20 characters" } post_id = { optional = true, default = "", regex = "^((?!://|://)[+a-zA-Z0-9])*$", 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" } +min_comment_length = { default = 1, optional = true, nmin = 0, nmax = 10000, type = "int", explanation = "min_comment_length number of characters a comment can have. default is 0", example = 50, oob_error = "the max comment length should be between 1 and 100" } post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr" } -min_comments = { default = 20, optional = false, nmin = 15, type = "int", explanation = "The minimum number of comments a post should have to be included. default is 20", example = 29, oob_error = "the minimum number of comments should be between 15 and 999999" } +min_comments = { default = 20, optional = false, nmin = 10, type = "int", explanation = "The minimum number of comments a post should have to be included. default is 20", example = 29, oob_error = "the minimum number of comments should be between 15 and 999999" } [settings] @@ -22,7 +23,7 @@ times_to_run = { optional = false, default = 1, example = 2, explanation = "Used 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" } transition = { optional = true, default = 0.2, example = 0.2, explanation = "Sets the transition time (in seconds) between the comments. Set to 0 if you want to disable it.", type = "float", nmin = 0, nmax = 2, oob_error = "The transition HAS to be between 0 and 2", input_error = "The opacity HAS to be a decimal number between 0 and 2" } storymode = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Only read out title and post content, not yet implemented" } - +storymodemethode= { optional = true, default = 1, example = 1, explanation = "Method to for storymode. Set to an int e.g.0 or 1", type = "int", nmin = 0, oob_error = "It's very hard to run something less than once." } [settings.background] background_choice = { optional = true, default = "minecraft", example = "rocket-league", options = ["minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck", ""], explanation = "Sets the background for the video based on game name" } diff --git a/utils/imagenarator.py b/utils/imagenarator.py new file mode 100644 index 0000000..2fc8571 --- /dev/null +++ b/utils/imagenarator.py @@ -0,0 +1,41 @@ +from PIL import Image, ImageDraw,ImageFont +import textwrap +import re + +def draw_multiple_line_text(image, text, font, text_color,padding): + ''' + Draw multiline text over given image + ''' + draw = ImageDraw.Draw(image) + Fontperm= font.getsize(text) + image_width, image_height = image.size + lines = textwrap.wrap(text, width=50) + y=(image_height/2)-(((Fontperm[1]+(len(lines)*padding)/len(lines))*len(lines))/2) #dont touch it + for line in lines: + line_width, line_height = font.getsize(line) + draw.text(((image_width - line_width) / 2, y), + line, font=font, fill=text_color) + y += line_height + padding + + +def imagemaker(text, + reddit_obj, + idx='NoName', + padding=5 + ): + id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) + font=ImageFont.truetype("arial.ttf", 20) + Fontperm= font.getsize(text) + # print(Fontperm[1]) + size=(500,176) + + textcolor=(240,240,240) + + image =Image.new('RGBA',size,(33,33,36,255)) + + draw = ImageDraw.Draw(image) + if len(text)>50: + draw_multiple_line_text(image, text,font, textcolor,padding) + else: + draw.text(((image.size[0]-Fontperm[0])/2,(image.size[1]-Fontperm[1])/2),font=font,text=text,align='center') #(image.size[1]/2)-(Fontperm[1]/2) + image.save(f'assets/temp/{id}/png/img{idx}.png') \ No newline at end of file diff --git a/utils/posttextparser.py b/utils/posttextparser.py new file mode 100644 index 0000000..a0d936c --- /dev/null +++ b/utils/posttextparser.py @@ -0,0 +1,46 @@ + +MAX_CHARACTER= 200 + +#working good +def posttextparser(obj): + text=obj#["thread_post"] + newtext=[] + # for text in text: + if len(text)>MAX_CHARACTER: + text2=text.split("\n") + for dot in text2: + if len(dot)>MAX_CHARACTER: + text3=dot.split(".") + for comma in text3: + if len(comma)> MAX_CHARACTER: + text4=comma.split(',') + newtext.extend(text4) + else: + newtext.append(comma) + else: + newtext.append(dot) + else: + newtext.append(text) + return remover(newtext) + +def remover(List): + reg=['',' ','.','\n',')',"''",'"',"'",'"','""'] #add if any any unwant value found + lines=List + lines1=[] + lines2=[] + + for item in lines: + for r in reg : + if item==r: + break + else: + continue + else: + lines1.append(item) + + for a in lines1: #Double check + if a!='': + aa=a.strip() + lines2.append(aa) + # print(f'"{a}"') + return lines2 \ No newline at end of file diff --git a/utils/subreddit.py b/utils/subreddit.py index c386868..381f2c0 100644 --- a/utils/subreddit.py +++ b/utils/subreddit.py @@ -39,6 +39,9 @@ def get_subreddit_undone(submissions: list, subreddit, times_checked=0): f'This post has under the specified minimum of comments ({settings.config["reddit"]["thread"]["min_comments"]}). Skipping...' ) continue + if settings.config['settings']['storymode'] : + if not submission.is_self : + continue return submission print("all submissions have been done going by top submission order") VALID_TIME_FILTERS = [ diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 743e561..b1321cc 100755 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -74,10 +74,20 @@ def make_final_video( .resize(height=H) .crop(x1=1166.6, y1=0, x2=2246.6, y2=1920) ) - + # Gather all audio clips - audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/{i}.mp3") for i in range(number_of_clips)] - audio_clips.insert(0, AudioFileClip(f"assets/temp/{id}/mp3/title.mp3")) + if settings.config["settings"]["storymode"]: + if settings.config["settings"]["storymodemethode"] == 0: + audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/title.mp3")] + audio_clips.insert(1,AudioFileClip(f"assets/temp/{id}/mp3/postaudio.mp3")) + elif settings.config["settings"]["storymodemethode"] == 1: + #here work is not done14 + audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/posttext-{i}.mp3") for i in range(number_of_clips+1)] + audio_clips.insert(0, AudioFileClip(f"assets/temp/{id}/mp3/title.mp3")) + + else: + audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/{i}.mp3") for i in range(number_of_clips)] + audio_clips.insert(0, AudioFileClip(f"assets/temp/{id}/mp3/title.mp3")) audio_concat = concatenate_audioclips(audio_clips) audio_composite = CompositeAudioClip([audio_concat]) @@ -96,27 +106,40 @@ def make_final_video( .crossfadein(new_transition) .crossfadeout(new_transition), ) - - for i in range(0, number_of_clips): - image_clips.append( - ImageClip(f"assets/temp/{id}/png/comment_{i}.png") - .set_duration(audio_clips[i + 1].duration) - .resize(width=W - 100) - .set_opacity(new_opacity) - .crossfadein(new_transition) - .crossfadeout(new_transition) - ) - - # if os.path.exists("assets/mp3/posttext.mp3"): - # image_clips.insert( - # 0, - # ImageClip("assets/png/title.png") - # .set_duration(audio_clips[0].duration + audio_clips[1].duration) - # .set_position("center") - # .resize(width=W - 100) - # .set_opacity(float(opacity)), - # ) - # else: story mode stuff + if settings.config["settings"]["storymode"]: + if settings.config["settings"]["storymodemethode"] == 0: + if os.path.exists(f"assets/temp/{id}/png/story_content.png"):# else: story mode stuff + image_clips.insert( + 1, + ImageClip(f"assets/temp/{id}/png/story_content.png") + .set_duration(audio_clips[1].duration) + .set_position("center") + .resize(width=W - 100) + .set_opacity(float(opacity)), + ) + elif settings.config["settings"]["storymodemethode"] == 1: + for i in range(0, number_of_clips+1): + image_clips.append( + ImageClip(f"assets/temp/{id}/png/img{i}.png") + .set_duration(audio_clips[i + 1].duration) + .resize(width=W - 100) + .set_opacity(new_opacity) + # .crossfadein(new_transition) + # .crossfadeout(new_transition) + ) + else : + for i in range(0, number_of_clips): + image_clips.append( + ImageClip(f"assets/temp/{id}/png/comment_{i}.png") + .set_duration(audio_clips[i + 1].duration) + .resize(width=W - 100) + .set_opacity(new_opacity) + .crossfadein(new_transition) + .crossfadeout(new_transition) + ) + + + img_clip_pos = background_config[3] image_concat = concatenate_videoclips(image_clips).set_position( img_clip_pos @@ -143,6 +166,7 @@ def make_final_video( final = Video(final).add_watermark( text=f"Background credit: {background_config[2]}", opacity=0.4, redditid=reddit_obj ) + final.write_videofile( f"assets/temp/{id}/temp.mp4", fps=30, @@ -156,7 +180,7 @@ def make_final_video( 0, length, targetname=f"results/{subreddit}/{filename}", - ) + ) save_data(subreddit, filename, title, idx, background_config[2]) print_step("Removing temporary files 🗑") cleanups = cleanup(id) diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index 2344fce..ddf05a6 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -11,10 +11,10 @@ from playwright.async_api import async_playwright # pylint: disable=unused-impo from playwright.sync_api import sync_playwright, ViewportSize from rich.progress import track import translators as ts - +from utils.imagenarator import imagemaker from utils.console import print_step, print_substep -storymode = False + def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): @@ -32,7 +32,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in with sync_playwright() as p: print_substep("Launching Headless Browser...") - browser = p.chromium.launch() + browser = p.chromium.launch() #headless=False #to check for chrome view context = browser.new_context() if settings.config["settings"]["theme"] == "dark": @@ -76,10 +76,18 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in postcontentpath = f"assets/temp/{id}/png/title.png" page.locator('[data-test-id="post-content"]').screenshot(path= postcontentpath) - if storymode: - page.locator('[data-click-id="text"]').screenshot( - path=f"assets/temp/{id}/png/story_content.png" - ) + if reddit_object["thread_post"] != "" and settings.config["settings"]["storymode"] == True: + if settings.config["settings"]["storymodemethode"] == 0: + try : #new change + page.locator('[data-click-id="text"]').first.screenshot( + path=f"assets/temp/{id}/png/story_content.png" + ) + except: + exit + elif settings.config["settings"]["storymodemethode"] == 1: + for idx,item in enumerate(reddit_object["thread_post"]): + imagemaker(item,idx=idx,reddit_obj=reddit_object) + else: for idx, comment in enumerate( track(reddit_object["comments"], "Downloading screenshots...")