diff --git a/.gitignore b/.gitignore index 41bdd5e..cc6bd18 100644 --- a/.gitignore +++ b/.gitignore @@ -231,7 +231,8 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -assets/ +assets/temp +assets/backgrounds /.vscode out .DS_Store @@ -244,4 +245,4 @@ video_creation/data/videos.json video_creation/data/envvars.txt config.toml -*.exe \ No newline at end of file +*.exe diff --git a/README.md b/README.md index 5fadd83..6f92026 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ I have tried to simplify the code so anyone can read it and start contributing a Please read our [contributing guidelines](CONTRIBUTING.md) for more detailed information. -### For any questions or support join the [Discord](https://discord.gg/Vkanmh6C8V) server +### For any questions or support join the [Discord](https://discord.gg/qfQSx45xCV) server ## Developers and maintainers. @@ -101,6 +101,8 @@ Freebiell (Freebie#3263) - https://github.com/FreebieII Aman Raza (electro199#8130) - https://github.com/electro199 +Cyteon (cyteon) - https://github.com/cyteon + ## LICENSE [Roboto Fonts](https://fonts.google.com/specimen/Roboto/about) are licensed under [Apache License V2](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/TTS/streamlabs_polly.py b/TTS/streamlabs_polly.py index 3f0610d..1bbbc2f 100644 --- a/TTS/streamlabs_polly.py +++ b/TTS/streamlabs_polly.py @@ -43,9 +43,11 @@ class StreamlabsPolly: f"Please set the config variable STREAMLABS_POLLY_VOICE to a valid voice. options are: {voices}" ) voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize() + body = {"voice": voice, "text": text, "service": "polly"} headers = {"Referer": "https://streamlabs.com/"} response = requests.post(self.url, headers=headers, data=body) + if not check_ratelimit(response): self.run(text, filepath, random_voice) diff --git a/assets/title_template.png b/assets/title_template.png new file mode 100644 index 0000000..d726d4f Binary files /dev/null and b/assets/title_template.png differ diff --git a/main.py b/main.py index 0a401c3..abedeeb 100755 --- a/main.py +++ b/main.py @@ -81,7 +81,7 @@ def shutdown() -> NoReturn: if __name__ == "__main__": - if sys.version_info.major != 3 or sys.version_info.minor != 10: + if sys.version_info.major != 3 or sys.version_info.minor not in [10, 11]: print( "Hey! Congratulations, you've made it so far (which is pretty rare with no Python 3.10). Unfortunately, this program only works on Python 3.10. Please install Python 3.10 and try again." ) diff --git a/reddit/subreddit.py b/reddit/subreddit.py index 8646b48..5f2ac5f 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -104,7 +104,7 @@ def get_subreddit_threads(POST_ID: str): upvotes = submission.score ratio = submission.upvote_ratio * 100 num_comments = submission.num_comments - threadurl = f"https://reddit.com{submission.permalink}" + threadurl = f"https://new.reddit.com/{submission.permalink}" print_substep(f"Video will be: {submission.title} :thumbsup:", style="bold green") print_substep(f"Thread url is: {threadurl} :thumbsup:", style="bold green") diff --git a/utils/.config.template.toml b/utils/.config.template.toml index 211cc97..f4a3af0 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -31,6 +31,7 @@ storymode_max_length = { optional = true, default = 1000, example = 1000, explan resolution_w = { optional = false, default = 1080, example = 1440, explantation = "Sets the width in pixels of the final video" } resolution_h = { optional = false, default = 1920, example = 2560, explantation = "Sets the height in pixels of the final video" } zoom = { optional = true, default = 1, example = 1.1, explanation = "Sets the browser zoom level. Useful if you want the text larger.", type = "float", nmin = 0.1, nmax = 2, oob_error = "The text is really difficult to read at a zoom level higher than 2" } +channel_name = { optional = true, default = "Reddit Tales", example = "Reddit Stories", explanation = "Sets the channel name for the video" } [settings.background] background_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck", "minecraft-2","multiversus","fall-guys","steep", ""], explanation = "Sets the background for the video based on game name" } diff --git a/utils/imagenarator.py b/utils/imagenarator.py index 151b0e6..6ed22cf 100644 --- a/utils/imagenarator.py +++ b/utils/imagenarator.py @@ -56,25 +56,17 @@ def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) -> """ Render Images for video """ - title = process_text(reddit_obj["thread_title"], False) texts = reddit_obj["thread_post"] id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) if transparent: font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100) - tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100) else: - tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100) # for title font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 100) size = (1920, 1080) image = Image.new("RGBA", size, theme) - # for title - draw_multiple_line_text(image, title, tfont, txtclr, padding, wrap=30, transparent=transparent) - - image.save(f"assets/temp/{id}/png/title.png") - for idx, text in track(enumerate(texts), "Rendering Image"): image = Image.new("RGBA", size, theme) text = process_text(text, False) diff --git a/video_creation/data/videos.json b/video_creation/data/videos.json deleted file mode 100644 index fe51488..0000000 --- a/video_creation/data/videos.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 5069474..0ebd39b 100644 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -3,14 +3,17 @@ import os import re import tempfile import threading +import textwrap import time + +from pathlib import Path from os.path import exists # Needs to be imported specifically from typing import Final from typing import Tuple, Dict import ffmpeg import translators -from PIL import Image +from PIL import ImageDraw, ImageFont, Image from rich.console import Console from rich.progress import track @@ -105,6 +108,41 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str: exit(1) return output_path +def create_fancy_thumbnail(image, text, text_color, padding, wrap=35): + print_step(f"Creating fancy thumbnail for: {text}") + font_title_size = 47 + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size) + image_width, image_height = image.size + lines = textwrap.wrap(text, width=wrap) + y = (image_height / 2) - (((font.getsize(text)[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) + 30 + draw = ImageDraw.Draw(image) + + username_font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 30) + draw.text((205, 825), settings.config["settings"]["channel_name"], font=username_font, fill=text_color, align="left") + + if len(lines) == 3: + lines = textwrap.wrap(text, width=wrap+10) + font_title_size = 40 + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size) + y = (image_height / 2) - (((font.getsize(text)[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) + 35 + elif len(lines) == 4: + lines = textwrap.wrap(text, width=wrap+10) + font_title_size = 35 + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size) + y = (image_height / 2) - (((font.getsize(text)[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) + 40 + elif len(lines) > 4: + lines = textwrap.wrap(text, width=wrap+10) + font_title_size = 30 + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size) + y = (image_height / 2) - (((font.getsize(text)[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) + 30 + + for line in lines: + _, line_height = font.getsize(line) + draw.text((120, y), line, font=font, fill=text_color, align="left") + y += line_height + padding + + return image + def merge_background_audio(audio: ffmpeg, reddit_id: str): """Gather an audio and merge with assets/backgrounds/background.mp3 @@ -201,6 +239,28 @@ def make_final_video( image_clips = list() + Path(f"assets/temp/{reddit_id}/png").mkdir(parents=True, exist_ok=True) + + # Copyright 2024 beingbored (aka. Tim), MIT License, permission granted to use, copy, modify, and distribute. + # get the title_template image and draw a text in the middle part of it with the title of the thread + title_template = Image.open("assets/title_template.png") + + title = reddit_obj["thread_title"] + + title = name_normalize(title) + + font_color = "#000000" + padding = 5 + + # create_fancy_thumbnail(image, text, text_color, padding + title_img = create_fancy_thumbnail( + title_template, title, font_color, padding + ) + + title_img.save(f"assets/temp/{reddit_id}/png/title.png") + + # Copyright end + image_clips.insert( 0, ffmpeg.input(f"assets/temp/{reddit_id}/png/title.png")["v"].filter( diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index cdcf8ef..da7e6e0 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -58,6 +58,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): bgcolor = (255, 255, 255, 255) txtcolor = (0, 0, 0) transparent = False + if storymode and settings.config["settings"]["storymodemethod"] == 1: # for idx,item in enumerate(reddit_object["thread_post"]): print_substep("Generating images...") @@ -73,7 +74,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): print_substep("Launching Headless Browser...") browser = p.chromium.launch( - headless=True + headless=False ) # headless=False will show the browser for debugging purposes # Device scale factor (or dsf for short) allows us to increase the resolution of the screenshots # When the dsf is 1, the width of the screenshot is 600 pixels @@ -98,9 +99,9 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): page.set_viewport_size(ViewportSize(width=1920, height=1080)) page.wait_for_load_state() - page.locator('[name="username"]').fill(settings.config["reddit"]["creds"]["username"]) - page.locator('[name="password"]').fill(settings.config["reddit"]["creds"]["password"]) - page.locator("button[class$='m-full-width']").click() + page.locator(f'input[name="username"]').fill(settings.config["reddit"]["creds"]["username"]) + page.locator(f'input[name="password"]').fill(settings.config["reddit"]["creds"]["password"]) + page.get_by_role("button", name="Log In").click() page.wait_for_timeout(5000) login_error_div = page.locator(".AnimatedForm__errorMessage").first @@ -218,7 +219,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): if page.locator('[data-testid="content-gate"]').is_visible(): page.locator('[data-testid="content-gate"] button').click() - page.goto(f'https://reddit.com{comment["comment_url"]}', timeout=0) + page.goto(f"https://new.reddit.com/{comment['comment_url']}") # translate code @@ -261,4 +262,4 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): # close browser instance when we are done using it browser.close() - print_substep("Screenshots downloaded Successfully.", style="bold green") + print_substep("Screenshots downloaded Successfully.", style="bold green") \ No newline at end of file