diff --git a/examples/final_video.mp4 b/examples/final_video.mp4 deleted file mode 100644 index cd19420..0000000 Binary files a/examples/final_video.mp4 and /dev/null differ diff --git a/reddit/subreddit.py b/reddit/subreddit.py index ed1e8cf..5c24c32 100644 --- a/reddit/subreddit.py +++ b/reddit/subreddit.py @@ -124,7 +124,7 @@ def get_subreddit_threads(POST_ID: str): threadurl = f"https://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") + print_substep(f"Thread url is: {threadurl} :thumbsup:", style="bold green") print_substep(f"Thread has {upvotes} upvotes", style="bold blue") print_substep(f"Thread has a upvote ratio of {ratio}%", style="bold blue") print_substep(f"Thread has {num_comments} comments", style="bold blue") diff --git a/requirements.txt b/requirements.txt index 6cf4689..22d3d7f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,4 +18,5 @@ clean-text==0.6.0 unidecode==1.3.2 spacy==3.4.1 torch==1.12.1 -transformers==4.25.1 \ No newline at end of file +transformers==4.25.1 +ffmpeg-python==0.2.0 \ No newline at end of file diff --git a/utils/.config.template.toml b/utils/.config.template.toml index ec5bb2a..b2fa1d4 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -22,14 +22,13 @@ ai_similarity_keywords = {optional = true, type="str", example= 'Elon Musk, Twit [settings] 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", ], explanation = "Sets the Reddit theme, either LIGHT or DARK" } +theme = { optional = false, default = "dark", example = "light", options = ["dark", "light", "transparent", ], explanation = "Sets the Reddit theme, either LIGHT or DARK. For story mode you can also use a transparent background." } 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" } 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, great for subreddits with stories" } storymodemethod= { optional = true, default = 1, example = 1, explanation = "Style that's used for the storymode. Set to 0 for single picture display in whole video, set to 1 for fancy looking video ", type = "int", nmin = 0, oob_error = "It's very hard to run something less than once.", options = [0, 1] } storymode_max_length = { optional = true, default = 1000, example = 1000, explanation = "Max length of the storymode video in characters. 200 characters are approximately 50 seconds.", type = "int", nmin = 1, oob_error = "It's very hard to make a video under a second." } -fps = { optional = false, default = 30, example = 30, explanation = "Sets the FPS of the video, 30 is default for best performance. 60 FPS is smoother.", type = "int", nmin = 1, nmax = 60, oob_error = "The FPS HAS to be between 1 and 60" } 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" } diff --git a/utils/imagenarator.py b/utils/imagenarator.py index 8e3789e..63d8df3 100644 --- a/utils/imagenarator.py +++ b/utils/imagenarator.py @@ -6,7 +6,8 @@ from PIL import Image, ImageDraw, ImageFont from rich.progress import track from TTS.engine_wrapper import process_text -def draw_multiple_line_text(image, text, font, text_color, padding, wrap=50) -> None: + +def draw_multiple_line_text(image, text, font, text_color, padding, wrap=50, transparent=False) -> None: """ Draw multiline text over given image """ @@ -14,63 +15,45 @@ def draw_multiple_line_text(image, text, font, text_color, padding, wrap=50) -> Fontperm = font.getsize(text) image_width, image_height = image.size lines = textwrap.wrap(text, width=wrap) - y = (image_height / 2) - ( - ((Fontperm[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2 - ) + y = (image_height / 2) - (((Fontperm[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) for line in lines: line_width, line_height = font.getsize(line) + if transparent: + shadowcolor = "black" + for i in range(1, 5): + draw.text(((image_width - line_width) / 2 - i, y - i), line, font=font, fill=shadowcolor) + draw.text(((image_width - line_width) / 2 + i, y - i), line, font=font, fill=shadowcolor) + draw.text(((image_width - line_width) / 2 - i, y + i), line, font=font, fill=shadowcolor) + draw.text(((image_width - line_width) / 2 + i, y + i), line, font=font, fill=shadowcolor) draw.text(((image_width - line_width) / 2, y), line, font=font, fill=text_color) y += line_height + padding -# theme=bgcolor,reddit_obj=reddit_object,txtclr=txtcolor -def imagemaker(theme, reddit_obj: dict, txtclr, padding=5) -> None: +def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) -> None: """ Render Images for video """ - title = process_text(reddit_obj["thread_title"], False) #TODO if second argument cause any error + title = process_text(reddit_obj["thread_title"], False) # TODO if second argument cause any error texts = reddit_obj["thread_post"] id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) - tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 27) # for title - font = ImageFont.truetype( - os.path.join("fonts", "Roboto-Regular.ttf"), 20 - ) # for despcription|comments - size = (500, 176) + if transparent: + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 50) + tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 50) + else: + tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 35) # for title + font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 30) + size = (1920, 1080) image = Image.new("RGBA", size, theme) - draw = ImageDraw.Draw(image) # for title - if len(title) > 40: - draw_multiple_line_text(image, title, tfont, txtclr, padding, wrap=30) - else: - - Fontperm = tfont.getsize(title) - draw.text( - ((image.size[0] - Fontperm[0]) / 2, (image.size[1] - Fontperm[1]) / 2), - font=tfont, - text=title, - ) # (image.size[1]/2)-(Fontperm[1]/2) + draw_multiple_line_text(image, title, tfont, txtclr, padding, wrap=30, transparent=transparent) image.save(f"assets/temp/{id}/png/title.png") - # for comment|description - - for idx, text in track(enumerate(texts), "Rendering Image"):#, total=len(texts)): - + for idx, text in track(enumerate(texts), "Rendering Image"): image = Image.new("RGBA", size, theme) - draw = ImageDraw.Draw(image) - text = process_text(text,False) - if len(text) > 50: - draw_multiple_line_text(image, text, font, txtclr, padding) - - else: - - Fontperm = font.getsize(text) - draw.text( - ((image.size[0] - Fontperm[0]) / 2, (image.size[1] - Fontperm[1]) / 2), - font=font, - text=text, - ) # (image.size[1]/2)-(Fontperm[1]/2) + text = process_text(text, False) + draw_multiple_line_text(image, text, font, txtclr, padding, wrap=30, transparent=transparent) image.save(f"assets/temp/{id}/png/img{idx}.png") diff --git a/utils/video.py b/utils/video.py deleted file mode 100644 index a785df4..0000000 --- a/utils/video.py +++ /dev/null @@ -1,68 +0,0 @@ -from __future__ import annotations - -import re -from typing import Tuple - -from PIL import ImageFont, Image, ImageDraw, ImageEnhance -from moviepy.video.VideoClip import VideoClip, ImageClip -from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip - - -class Video: - def __init__(self, video: VideoClip, *args, **kwargs): - self.video: VideoClip = video - self.fps = self.video.fps - self.duration = self.video.duration - - @staticmethod - def _create_watermark(text, redditid, fontsize, opacity=0.5): - id = re.sub(r"[^\w\s-]", "", redditid["thread_id"]) - path = f"./assets/temp/{id}/png/watermark.png" - width = int(fontsize * len(text)) - height = int(fontsize * len(text) / 2) - white = (255, 255, 255) - transparent = (0, 0, 0, 0) - - font = ImageFont.load_default() - wm = Image.new("RGBA", (width, height), transparent) - im = Image.new("RGBA", (width, height), transparent) # Change this line too. - - draw = ImageDraw.Draw(wm) - w, h = draw.textsize(text, font) - draw.text(((width - w) / 2, (height - h) / 2), text, white, font) - en = ImageEnhance.Brightness(wm) # todo allow it to use the fontsize - mask = en.enhance(1 - opacity) - im.paste(wm, (25, 25), mask) - im.save(path) - return ImageClip(path) - - def add_watermark( - self, - text, - redditid, - opacity=0.5, - duration: int | float = 5, - position: Tuple = (0.7, 0.9), - fontsize=15, - ): - compensation = round( - ( - position[0] - / ((len(text) * (fontsize / 5) / 1.5) / 100 + position[0] * position[0]) - ), - ndigits=2, - ) - position = (compensation, position[1]) - # print(f'{compensation=}') - # print(f'{position=}') - img_clip = self._create_watermark( - text, redditid, fontsize=fontsize, opacity=opacity - ) - img_clip = img_clip.set_opacity(opacity).set_duration(duration) - img_clip = img_clip.set_position( - position, relative=True - ) # todo get dara from utils/CONSTANTS.py and adapt position accordingly - - # Overlay the img clip on the first video clip - self.video = CompositeVideoClip([self.video, img_clip]) - return self.video diff --git a/video_creation/final_video.py b/video_creation/final_video.py old mode 100755 new mode 100644 index ad5ff95..ec4ac5c --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -1,33 +1,67 @@ +import multiprocessing import os import re -import multiprocessing -from os.path import exists -from typing import Tuple, Any, Final -import translators as ts import shutil +from os.path import exists +from typing import Final from typing import Tuple, Any -from PIL import Image -from moviepy.audio.AudioClip import concatenate_audioclips, CompositeAudioClip -from moviepy.audio.io.AudioFileClip import AudioFileClip -from moviepy.video.VideoClip import ImageClip -from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip -from moviepy.video.compositing.concatenate import concatenate_videoclips -from moviepy.video.io.VideoFileClip import VideoFileClip -from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip +import ffmpeg +import translators as ts +from PIL import Image from rich.console import Console from rich.progress import track +from utils import settings from utils.cleanup import cleanup from utils.console import print_step, print_substep -from utils.video import Video -from utils.videos import save_data -from utils.thumbnail import create_thumbnail -from utils import settings from utils.thumbnail import create_thumbnail +from utils.videos import save_data console = Console() +import tempfile +import threading +import time + + +class ProgressFfmpeg(threading.Thread): + def __init__(self, vid_duration_seconds, progress_update_callback): + threading.Thread.__init__(self, name='ProgressFfmpeg') + self.stop_event = threading.Event() + self.output_file = tempfile.NamedTemporaryFile(mode='w+', delete=False) + self.vid_duration_seconds = vid_duration_seconds + self.progress_update_callback = progress_update_callback + + def run(self): + + while not self.stop_event.is_set(): + latest_progress = self.get_latest_ms_progress() + if latest_progress is not None: + completed_percent = latest_progress / self.vid_duration_seconds + self.progress_update_callback(completed_percent) + time.sleep(1) + + def get_latest_ms_progress(self): + lines = self.output_file.readlines() + + if lines: + for line in lines: + if 'out_time_ms' in line: + out_time_ms = line.split('=')[1] + return int(out_time_ms) / 1000000.0 + return None + + def stop(self): + self.stop_event.set() + + def __enter__(self): + self.start() + return self + + def __exit__(self, *args, **kwargs): + self.stop() + def name_normalize(name: str) -> str: name = re.sub(r'[?\\"%*:|<>]', "", name) @@ -46,29 +80,20 @@ def name_normalize(name: str) -> str: return name -def prepare_background(reddit_id: str, W: int, H: int) -> VideoFileClip: - clip = ( - VideoFileClip(f"assets/temp/{reddit_id}/background.mp4") - .without_audio() - .resize(height=H) - ) - - # calculate the center of the background clip - c = clip.w // 2 - - # calculate the coordinates where to crop - half_w = W // 2 - x1 = c - half_w - x2 = c + half_w - - return clip.crop(x1=x1, y1=0, x2=x2, y2=H) +def prepare_background(reddit_id: str, W: int, H: int) -> str: + output_path = f"assets/temp/{reddit_id}/background_noaudio.mp4" + output = ffmpeg.input(f"assets/temp/{reddit_id}/background.mp4").filter('crop', f"ih*({W}/{H})", "ih").output( + output_path, an=None, + **{"c:v": "h264", "b:v": "20M", "b:a": "192k", "threads": multiprocessing.cpu_count()}).overwrite_output() + output.run(quiet=True) + return output_path def make_final_video( - number_of_clips: int, - length: int, - reddit_obj: dict, - background_config: Tuple[str, str, str, Any], + 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: @@ -81,103 +106,93 @@ def make_final_video( W: Final[int] = int(settings.config["settings"]["resolution_w"]) H: Final[int] = int(settings.config["settings"]["resolution_h"]) - # try: # if it isn't found (i.e you just updated and copied over config.toml) it will throw an error - # VOLUME_MULTIPLIER = settings.config["settings"]['background']["background_audio_volume"] - # except (TypeError, KeyError): - # print('No background audio volume found in config.toml. Using default value of 1.') - # VOLUME_MULTIPLIER = 1 - reddit_id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) print_step("Creating the final video 🎥") - VideoFileClip.reW = lambda clip: clip.resize(width=W) - VideoFileClip.reH = lambda clip: clip.resize(width=H) - - opacity = settings.config["settings"]["opacity"] - transition = settings.config["settings"]["transition"] - - background_clip = prepare_background(reddit_id, W=W, H=H) + background_clip = ffmpeg.input(prepare_background(reddit_id, W=W, H=H)) # Gather all audio clips + audio_clips = list() if settings.config["settings"]["storymode"]: if settings.config["settings"]["storymodemethod"] == 0: - audio_clips = [AudioFileClip(f"assets/temp/{reddit_id}/mp3/title.mp3")] - audio_clips.insert(1, AudioFileClip(f"assets/temp/{reddit_id}/mp3/postaudio.mp3")) + audio_clips = [ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")] + audio_clips.insert(1, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/postaudio.mp3")) elif settings.config["settings"]["storymodemethod"] == 1: audio_clips = [ - AudioFileClip(f"assets/temp/{reddit_id}/mp3/postaudio-{i}.mp3") + ffmpeg.input(f"assets/temp/{reddit_id}/mp3/postaudio-{i}.mp3") for i in track( range(number_of_clips + 1), "Collecting the audio files..." ) ] - audio_clips.insert(0, AudioFileClip(f"assets/temp/{reddit_id}/mp3/title.mp3")) + audio_clips.insert(0, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")) else: - audio_clips = [ - AudioFileClip(f"assets/temp/{reddit_id}/mp3/{i}.mp3") - for i in range(number_of_clips) - ] - audio_clips.insert(0, AudioFileClip(f"assets/temp/{reddit_id}/mp3/title.mp3")) - audio_concat = concatenate_audioclips(audio_clips) - audio_composite = CompositeAudioClip([audio_concat]) + audio_clips = [ffmpeg.input(f"assets/temp/{reddit_id}/mp3/{i}.mp3") for i in range(number_of_clips)] + audio_clips.insert(0, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")) + + audio_clips_durations = [float(ffmpeg.probe(f"assets/temp/{reddit_id}/mp3/{i}.mp3")['format']['duration']) for i + in + range(number_of_clips)] + audio_clips_durations.insert(0, float( + ffmpeg.probe(f"assets/temp/{reddit_id}/mp3/title.mp3")['format']['duration'])) + audio_concat = ffmpeg.concat(*audio_clips, a=1, v=0) + ffmpeg.output(audio_concat, f"assets/temp/{reddit_id}/audio.mp3", **{"b:a": "192k"}).overwrite_output().run( + quiet=True) console.log(f"[bold green] Video Will Be: {length} Seconds Long") - # add title to video - image_clips = [] - # Gather all images - new_opacity = 1 if opacity is None or float(opacity) >= 1 else float(opacity) - new_transition = ( - 0 if transition is None or float(transition) > 2 else float(transition) - ) - screenshot_width = int((W * 90) // 100) + # Create a screenshot_width variable to scale the screenshots to the correct size, the calculation is int((W * 90) // 100) + # Convert it to a ffmpeg one with iw- + screenshot_width = int((W * 45) // 100) + audio = ffmpeg.input(f"assets/temp/{reddit_id}/audio.mp3") + + image_clips = list() + image_clips.insert( 0, - ImageClip(f"assets/temp/{reddit_id}/png/title.png") - .set_duration(audio_clips[0].duration) - .resize(width=screenshot_width) - .set_opacity(new_opacity) - .crossfadein(new_transition) - .crossfadeout(new_transition), + ffmpeg.input(f"assets/temp/{reddit_id}/png/title.png")['v'] + .filter('scale', screenshot_width, -1) ) + + current_time = 0 if settings.config["settings"]["storymode"]: + audio_clips_durations = [ + float(ffmpeg.probe(f"assets/temp/{reddit_id}/mp3/postaudio-{i}.mp3")['format']['duration']) for i in + range(number_of_clips)] + audio_clips_durations.insert(0, float( + ffmpeg.probe(f"assets/temp/{reddit_id}/mp3/title.mp3")['format']['duration'])) if settings.config["settings"]["storymodemethod"] == 0: image_clips.insert( 1, - ImageClip(f"assets/temp/{reddit_id}/png/story_content.png") - .set_duration(audio_clips[1].duration) - .set_position("center") - .resize(width=screenshot_width) - .set_opacity(float(opacity)), + ffmpeg.input(f"assets/temp/{reddit_id}/png/story_content.png") + .filter('scale', screenshot_width, -1) ) + background_clip = background_clip.overlay(image_clips[1], + enable=f'between(t,{current_time},{current_time + audio_clips_durations[1]})', + x='(main_w-overlay_w)/2', y='(main_h-overlay_h)/2') + current_time += audio_clips_durations[1] elif settings.config["settings"]["storymodemethod"] == 1: for i in track( - range(0, number_of_clips + 1), "Collecting the image files..." + range(0, number_of_clips + 1), "Collecting the image files..." ): image_clips.append( - ImageClip(f"assets/temp/{reddit_id}/png/img{i}.png") - .set_duration(audio_clips[i + 1].duration) - .resize(width=screenshot_width) - .set_opacity(new_opacity) - # .crossfadein(new_transition) - # .crossfadeout(new_transition) + ffmpeg.input(f"assets/temp/{reddit_id}/png/img{i}.png")['v'] + .filter('scale', 1080, -1) ) + background_clip = background_clip.overlay(image_clips[i], + enable=f'between(t,{current_time},{current_time + audio_clips_durations[i]})', + x='(main_w-overlay_w)/2', y='(main_h-overlay_h)/2') + current_time += audio_clips_durations[i] else: - for i in range(0, number_of_clips): + for i in range(0, number_of_clips + 1): image_clips.append( - ImageClip(f"assets/temp/{reddit_id}/png/comment_{i}.png") - .set_duration(audio_clips[i + 1].duration) - .resize(width=screenshot_width) - .set_opacity(new_opacity) - .crossfadein(new_transition) - .crossfadeout(new_transition) + ffmpeg.input(f"assets/temp/{reddit_id}/png/comment_{i}.png")['v'] + .filter('scale', screenshot_width, -1) ) + background_clip = background_clip.overlay(image_clips[i], + enable=f'between(t,{current_time},{current_time + audio_clips_durations[i]})', + x='(main_w-overlay_w)/2', y='(main_h-overlay_h)/2') + current_time += audio_clips_durations[i] - img_clip_pos = background_config[3] - image_concat = concatenate_videoclips(image_clips).set_position( - img_clip_pos - ) # note transition kwarg for delay in imgs - 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"]) title_thumb = reddit_obj["thread_title"] @@ -189,7 +204,7 @@ def make_final_video( print_substep("The results folder didn't exist so I made it") os.makedirs(f"./results/{subreddit}") - # create a tumbnail for the video + # create a thumbnail for the video settingsbackground = settings.config["settings"]["background"] if settingsbackground["background_thumbnail"]: @@ -209,18 +224,47 @@ def make_final_video( if first_image is None: print_substep("No png files found in assets/backgrounds", "red") - if settingsbackground["background_thumbnail"] and first_image: - font_family = settingsbackground["background_thumbnail_font_family"] - font_size = settingsbackground["background_thumbnail_font_size"] - font_color = settingsbackground["background_thumbnail_font_color"] - thumbnail = Image.open(f"assets/backgrounds/{first_image}") - width, height = thumbnail.size - thumbnailSave = create_thumbnail(thumbnail, font_family, font_size, font_color, width, height, title_thumb) - thumbnailSave.save(f"./assets/temp/{reddit_id}/thumbnail.png") - print_substep(f"Thumbnail - Building Thumbnail in assets/temp/{reddit_id}/thumbnail.png") + else: + font_family = settingsbackground["background_thumbnail_font_family"] + font_size = settingsbackground["background_thumbnail_font_size"] + font_color = settingsbackground["background_thumbnail_font_color"] + thumbnail = Image.open(f"assets/backgrounds/{first_image}") + width, height = thumbnail.size + thumbnailSave = create_thumbnail(thumbnail, font_family, font_size, font_color, width, height, title_thumb) + thumbnailSave.save(f"./assets/temp/{reddit_id}/thumbnail.png") + print_substep(f"Thumbnail - Building Thumbnail in assets/temp/{reddit_id}/thumbnail.png") + + text = f"Background by {background_config[2]}" + background_clip = ffmpeg.drawtext(background_clip, + text=text, + x=f'(w-text_w)', y=f'(h-text_h)', + fontsize=12, + fontcolor="White", + fontfile=os.path.join("fonts", "Roboto-Regular.ttf")) + print_step("Rendering the video 🎥") + from tqdm import tqdm + pbar = tqdm(total=100, desc="Progress: ", bar_format="{l_bar}{bar}", unit=" %") + + def on_update_example(progress): + status = round(progress * 100, 2) + old_percentage = pbar.n + pbar.update(status - old_percentage) + + position = W // 2, H - 20 + ffmpeg.filter(filter_name='drawtext', stream_spec=background_clip, text=text, fontfile='fonts/Roboto-Regular.ttf', fontsize=12, + fontcolor='white', x=position[0], y=position[1], box=1) + with ProgressFfmpeg(length, on_update_example) as progress: + ffmpeg.output(background_clip, audio, f"results/{subreddit}/{filename}.mp4", f='mp4', + **{"c:v": "h264", "b:v": "20M", "b:a": "192k", + "threads": multiprocessing.cpu_count()}).overwrite_output().global_args('-progress', + progress.output_file.name).run( + quiet=True, overwrite_output=True, capture_stdout=False, capture_stderr=False) + + old_percentage = pbar.n + pbar.update(100 - old_percentage) + pbar.close() + - # create a tumbnail for the video - settingsbackground = settings.config["settings"]["background"] if settingsbackground["background_thumbnail"]: if not exists(f"./results/{subreddit}/thumbnails"): @@ -249,39 +293,11 @@ def make_final_video( thumbnailSave.save(f"./assets/temp/{reddit_id}/thumbnail.png") print_substep(f"Thumbnail - Building Thumbnail in assets/temp/{reddit_id}/thumbnail.png") - # if settings.config["settings"]['background']["background_audio"] and exists(f"assets/backgrounds/background.mp3"): - # audioclip = mpe.AudioFileClip(f"assets/backgrounds/background.mp3").set_duration(final.duration) - # audioclip = audioclip.fx( volumex, 0.2) - # final_audio = mpe.CompositeAudioClip([final.audio, audioclip]) - # # lowered_audio = audio_background.multiply_volume( # todo get this to work - # # VOLUME_MULTIPLIER) # lower volume by background_audio_volume, use with fx - # final.set_audio(final_audio) - - final = Video(final).add_watermark( - text=f"Background credit: {background_config[2]}", - opacity=0.4, - redditid=reddit_obj, - ) - final.write_videofile( - f"assets/temp/{reddit_id}/temp.mp4", - fps=int(settings.config["settings"]["fps"]), - audio_codec="aac", - audio_bitrate="192k", - verbose=False, - threads=multiprocessing.cpu_count(), - #preset="ultrafast", # for testing purposes - ) - ffmpeg_extract_subclip( - f"assets/temp/{reddit_id}/temp.mp4", - 0, - length, - targetname=f"results/{subreddit}/{filename}.mp4", - ) - #get the thumbnail image from assets/temp/id/thumbnail.png and save it in results/subreddit/thumbnails - if settingsbackground["background_thumbnail"] and exists(f"assets/temp/{id}/thumbnail.png"): - shutil.move(f"assets/temp/{id}/thumbnail.png", f"./results/{subreddit}/thumbnails/{filename}.png") + # get the thumbnail image from assets/temp/id/thumbnail.png and save it in results/subreddit/thumbnails + if settingsbackground["background_thumbnail"] and exists(f"assets/temp/{reddit_id}/thumbnail.png"): + shutil.move(f"assets/temp/{reddit_id}/thumbnail.png", f"./results/{subreddit}/thumbnails/{filename}.png") - save_data(subreddit, filename+".mp4", title, idx, background_config[2]) + save_data(subreddit, filename + ".mp4", title, idx, background_config[2]) print_step("Removing temporary files 🗑") cleanups = cleanup(reddit_id) print_substep(f"Removed {cleanups} temporary files 🗑") diff --git a/video_creation/screenshot_downloader.py b/video_creation/screenshot_downloader.py index c98fb56..da2adbf 100644 --- a/video_creation/screenshot_downloader.py +++ b/video_creation/screenshot_downloader.py @@ -59,15 +59,31 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): ) bgcolor = (33, 33, 36, 255) txtcolor = (240, 240, 240) + transparent = False + elif settings.config["settings"]["theme"] == "transparent": + if storymode: + # Transparent theme + bgcolor = (0, 0, 0, 0) + txtcolor = (255, 255, 255) + transparent = True + else: + # Switch to dark theme + cookie_file = open( + "./video_creation/data/cookie-dark-mode.json", encoding="utf-8" + ) + bgcolor = (33, 33, 36, 255) + txtcolor = (240, 240, 240) + transparent = False else: cookie_file = open( "./video_creation/data/cookie-light-mode.json", encoding="utf-8" ) 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"]): - return imagemaker(theme=bgcolor, reddit_obj=reddit_object, txtclr=txtcolor) + return imagemaker(theme=bgcolor, reddit_obj=reddit_object, txtclr=txtcolor, transparent=transparent) cookies = json.load(cookie_file) cookie_file.close()