Add the option to customize the resolution of the video

pull/1277/head
John Toniutti 3 years ago
parent dcd0ff5b12
commit b3e288ca21
No known key found for this signature in database
GPG Key ID: 3C5434E3920EE1E1

@ -21,8 +21,9 @@ theme = { optional = false, default = "dark", example = "light", options = ["dar
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." } 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" } 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" } 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" } storymode = { optional = false, type = "bool", default = false, example = false, options = [true, false, ], explanation = "Only read out title and post content, not yet implemented" }
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" }
[settings.background] [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" } 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" }

@ -3,7 +3,7 @@ import multiprocessing
import os import os
import re import re
from os.path import exists from os.path import exists
from typing import Tuple, Any from typing import Tuple, Any, Final
from moviepy.audio.AudioClip import concatenate_audioclips, CompositeAudioClip from moviepy.audio.AudioClip import concatenate_audioclips, CompositeAudioClip
from moviepy.audio.io.AudioFileClip import AudioFileClip from moviepy.audio.io.AudioFileClip import AudioFileClip
@ -21,7 +21,6 @@ from utils.video import Video
from utils.videos import save_data from utils.videos import save_data
console = Console() console = Console()
W, H = 1080, 1920
def name_normalize(name: str) -> str: def name_normalize(name: str) -> str:
@ -45,6 +44,20 @@ def name_normalize(name: str) -> str:
return name return name
def prepare_background(id: str, W: int, H: int) -> VideoFileClip:
clip = VideoFileClip(f"assets/temp/{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 make_final_video( def make_final_video(
number_of_clips: int, number_of_clips: int,
length: int, length: int,
@ -58,23 +71,26 @@ def make_final_video(
reddit_obj (dict): The reddit object that contains the posts to read. reddit_obj (dict): The reddit object that contains the posts to read.
background_config (Tuple[str, str, str, Any]): The background config to use. background_config (Tuple[str, str, str, Any]): The background config to use.
""" """
# settings values
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 # 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"] # VOLUME_MULTIPLIER = settings.config["settings"]['background']["background_audio_volume"]
# except (TypeError, KeyError): # except (TypeError, KeyError):
# print('No background audio volume found in config.toml. Using default value of 1.') # print('No background audio volume found in config.toml. Using default value of 1.')
# VOLUME_MULTIPLIER = 1 # VOLUME_MULTIPLIER = 1
id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
print_step("Creating the final video 🎥") print_step("Creating the final video 🎥")
VideoFileClip.reW = lambda clip: clip.resize(width=W) VideoFileClip.reW = lambda clip: clip.resize(width=W)
VideoFileClip.reH = lambda clip: clip.resize(width=H) VideoFileClip.reH = lambda clip: clip.resize(width=H)
opacity = settings.config["settings"]["opacity"] opacity = settings.config["settings"]["opacity"]
transition = settings.config["settings"]["transition"] transition = settings.config["settings"]["transition"]
background_clip = (
VideoFileClip(f"assets/temp/{id}/background.mp4") background_clip = prepare_background(id, W=W, H=H)
.without_audio()
.resize(height=H)
.crop(x1=1166.6, y1=0, x2=2246.6, y2=1920)
)
# Gather all audio clips # Gather all audio clips
audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/{i}.mp3") for i in range(number_of_clips)] audio_clips = [AudioFileClip(f"assets/temp/{id}/mp3/{i}.mp3") for i in range(number_of_clips)]
@ -88,11 +104,12 @@ def make_final_video(
# Gather all images # Gather all images
new_opacity = 1 if opacity is None or float(opacity) >= 1 else float(opacity) 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) new_transition = 0 if transition is None or float(transition) > 2 else float(transition)
screenshow_width = int((W*90)//100)
image_clips.insert( image_clips.insert(
0, 0,
ImageClip(f"assets/temp/{id}/png/title.png") ImageClip(f"assets/temp/{id}/png/title.png")
.set_duration(audio_clips[0].duration) .set_duration(audio_clips[0].duration)
.resize(width=W - 100) .resize(width=screenshow_width)
.set_opacity(new_opacity) .set_opacity(new_opacity)
.crossfadein(new_transition) .crossfadein(new_transition)
.crossfadeout(new_transition), .crossfadeout(new_transition),
@ -102,7 +119,7 @@ def make_final_video(
image_clips.append( image_clips.append(
ImageClip(f"assets/temp/{id}/png/comment_{i}.png") ImageClip(f"assets/temp/{id}/png/comment_{i}.png")
.set_duration(audio_clips[i + 1].duration) .set_duration(audio_clips[i + 1].duration)
.resize(width=W - 100) .resize(width=screenshow_width)
.set_opacity(new_opacity) .set_opacity(new_opacity)
.crossfadein(new_transition) .crossfadein(new_transition)
.crossfadeout(new_transition) .crossfadeout(new_transition)
@ -118,6 +135,7 @@ def make_final_video(
# .set_opacity(float(opacity)), # .set_opacity(float(opacity)),
# ) # )
# else: story mode stuff # else: story mode stuff
img_clip_pos = background_config[3] 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 = concatenate_videoclips(image_clips).set_position(img_clip_pos) # note transition kwarg for delay in imgs
image_concat.audio = audio_composite image_concat.audio = audio_composite
@ -139,6 +157,7 @@ def make_final_video(
# # lowered_audio = audio_background.multiply_volume( # todo get this to work # # lowered_audio = audio_background.multiply_volume( # todo get this to work
# # VOLUME_MULTIPLIER) # lower volume by background_audio_volume, use with fx # # VOLUME_MULTIPLIER) # lower volume by background_audio_volume, use with fx
# final.set_audio(final_audio) # final.set_audio(final_audio)
final = Video(final).add_watermark(text=f"Background credit: {background_config[2]}", opacity=0.4, redditid=reddit_obj) final = Video(final).add_watermark(text=f"Background credit: {background_config[2]}", opacity=0.4, redditid=reddit_obj)
final.write_videofile( final.write_videofile(
f"assets/temp/{id}/temp.mp4", f"assets/temp/{id}/temp.mp4",

@ -1,7 +1,7 @@
import json
import re import re
import json
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Final
import translators as ts import translators as ts
from playwright.sync_api import sync_playwright, ViewportSize from playwright.sync_api import sync_playwright, ViewportSize
@ -10,9 +10,8 @@ from rich.progress import track
from utils import settings from utils import settings
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
# do not remove the above line
storymode = False __all__ = ["download_screenshots_of_reddit_posts"]
def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
@ -22,6 +21,12 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in
reddit_object (Dict): Reddit object received from reddit/subreddit.py reddit_object (Dict): Reddit object received from reddit/subreddit.py
screenshot_num (int): Number of screenshots to download screenshot_num (int): Number of screenshots to download
""" """
# settings values
W: Final[int] = int(settings.config["settings"]["resolution_w"])
H: Final[int] = int(settings.config["settings"]["resolution_h"])
lang: Final[str] = settings.config["reddit"]["thread"]["post_lang"]
storymode: Final[bool] = settings.config["settings"]["storymode"]
print_step("Downloading screenshots of reddit posts...") print_step("Downloading screenshots of reddit posts...")
id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"]) id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"])
# ! Make sure the reddit screenshots folder exists # ! Make sure the reddit screenshots folder exists
@ -31,18 +36,32 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in
print_substep("Launching Headless Browser...") print_substep("Launching Headless Browser...")
browser = p.chromium.launch(headless=True) # add headless=False for debug browser = p.chromium.launch(headless=True) # add headless=False for debug
context = browser.new_context()
# 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
# so we need a dsf such that the width of the screenshot is greater than the final resolution of the video
dsf = (W // 600)+1
context = browser.new_context(
locale=lang or "en-us",
color_scheme="dark",
viewport=ViewportSize(width=W, height=H),
device_scale_factor=dsf
)
# set the theme and disable non-essential cookies
if settings.config["settings"]["theme"] == "dark": if settings.config["settings"]["theme"] == "dark":
cookie_file = open("./video_creation/data/cookie-dark-mode.json", encoding="utf-8") cookie_file = open("./video_creation/data/cookie-dark-mode.json", encoding="utf-8")
else: else:
cookie_file = open("./video_creation/data/cookie-light-mode.json", encoding="utf-8") cookie_file = open("./video_creation/data/cookie-light-mode.json", encoding="utf-8")
cookies = json.load(cookie_file) cookies = json.load(cookie_file)
context.add_cookies(cookies) # load preference cookies context.add_cookies(cookies) # load preference cookies
# Get the thread screenshot # Get the thread screenshot
page = context.new_page() page = context.new_page()
page.goto(reddit_object["thread_url"], timeout=0) page.goto(reddit_object["thread_url"], timeout=0)
page.set_viewport_size(ViewportSize(width=1920, height=1080)) page.set_viewport_size(ViewportSize(width=W, height=H))
if page.locator('[data-testid="content-gate"]').is_visible(): if page.locator('[data-testid="content-gate"]').is_visible():
# This means the post is NSFW and requires to click the proceed button. # This means the post is NSFW and requires to click the proceed button.
@ -55,11 +74,11 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in
# translate code # translate code
if settings.config["reddit"]["thread"]["post_lang"]: if lang:
print_substep("Translating post...") print_substep("Translating post...")
texts_in_tl = ts.google( texts_in_tl = ts.google(
reddit_object["thread_title"], reddit_object["thread_title"],
to_language=settings.config["reddit"]["thread"]["post_lang"], to_language=lang,
) )
page.evaluate( page.evaluate(
@ -75,7 +94,7 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in
if storymode: if storymode:
page.locator('[data-click-id="text"]').screenshot(path=f"assets/temp/{id}/png/story_content.png") page.locator('[data-click-id="text"]').screenshot(path=f"assets/temp/{id}/png/story_content.png")
else: else:
for idx, comment in enumerate(track(reddit_object["comments"], "Downloading screenshots...")): for idx, comment in enumerate(track(reddit_object["comments"][:screenshot_num], "Downloading screenshots...")):
# Stop if we have reached the screenshot_num # Stop if we have reached the screenshot_num
if idx >= screenshot_num: if idx >= screenshot_num:
break break
@ -103,4 +122,8 @@ def download_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: in
screenshot_num += 1 screenshot_num += 1
print("TimeoutError: Skipping screenshot...") print("TimeoutError: Skipping screenshot...")
continue continue
# 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")

Loading…
Cancel
Save