Merge pull request #879 from elebumm/feat/RedditPostPosition

Feat/reddit post position
pull/885/head
Jason 2 years ago committed by GitHub
commit 8be3037fe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,15 +3,13 @@ client_id = { optional = false, nmin = 12, nmax = 30, explanation = "the ID of y
client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." } client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "the SECRET of your Reddit app of SCRIPT type", example = "fFAGRNJru1FTz70BzhT3Zg", regex = "^[-a-zA-Z0-9._~+/]+=*$", input_error = "The client ID can only contain printable characters.", oob_error = "The secret should be over 20 and under 40 characters, double check your input." }
username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "JasonLovesDoggo", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" } username = { optional = false, nmin = 3, nmax = 20, explanation = "the username of your reddit account", example = "JasonLovesDoggo", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" }
password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" } password = { optional = false, nmin = 8, explanation = "the password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" }
2fa = { optional = true, type = "bool", options = [ 2fa = { optional = true, type = "bool", options = [true,
true,
false, false,
], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true } ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true }
[reddit.thread] [reddit.thread]
random = { optional = true, options = [ random = { optional = true, options = [true,
true,
false, false,
], default = false, type = "bool", explanation = "If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: 'False'", example = "True" } ], default = false, type = "bool", explanation = "If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: 'False'", example = "True" }
subreddit = { optional = false, regex = "[_0-9a-zA-Z]+$", nmin = 3, nmax = 21, explanation = "what subreddit to pull posts from, the name of the sub, not the URL", example = "AskReddit", oob_error = "A subreddit name HAS to be between 3 and 20 characters" } subreddit = { optional = false, regex = "[_0-9a-zA-Z]+$", nmin = 3, nmax = 21, explanation = "what subreddit to pull posts from, the name of the sub, not the URL", example = "AskReddit", oob_error = "A subreddit name HAS to be between 3 and 20 characters" }
@ -20,21 +18,18 @@ max_comment_length = { default = 500, optional = false, nmin = 10, nmax = 10000,
post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr" } post_lang = { default = "", optional = true, explanation = "The language you would like to translate to.", example = "es-cr" }
[settings] [settings]
allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [ allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [true,
true,
false, false,
], explanation = "Whether to allow NSFW content, True or False" } ], explanation = "Whether to allow NSFW content, True or False" }
theme = { optional = false, default = "dark", example = "light", options = [ theme = { optional = false, default = "dark", example = "light", options = ["dark",
"dark",
"light", "light",
], explanation = "sets the Reddit theme, either LIGHT or DARK" } ], explanation = "sets the Reddit theme, either LIGHT or DARK" }
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" }
storymode = { optional = true, type = "bool", default = false, example = false, options = [ storymode = { optional = true, type = "bool", default = false, example = false, options = [true,
true,
false, false,
] } ] }
background_choice = { optional = true, default = "minecraft", example = "minecraft", options = ["minecraft", "gta", "rocket-league", "motor-gta"], explanation = "Sets the background for the video" }
[settings.tts] [settings.tts]
choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", ], example = "streamlabspolly", explanation = "The backend used for TTS generation. This can be left blank and you will be prompted to choose at runtime." } choice = { optional = false, default = "", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", ], example = "streamlabspolly", explanation = "The backend used for TTS generation. This can be left blank and you will be prompted to choose at runtime." }

@ -75,12 +75,10 @@ Please read our [contributing guidelines](CONTRIBUTING.md) for more detailed inf
Elebumm (Lewis#6305) - https://github.com/elebumm (Founder) Elebumm (Lewis#6305) - https://github.com/elebumm (Founder)
Jason (JasonLovesDoggo#1904) - https://github.com/JasonLovesDoggo Jason (JasonLovesDoggo#1904) - https://github.com/JasonLovesDoggo (Maintainer)
CallumIO (c.#6837) - https://github.com/CallumIO CallumIO (c.#6837) - https://github.com/CallumIO
HarryDaDev (hrvyy#9677) - https://github.com/ImmaHarry Verq (Verq#2338) - https://github.com/CordlessCoder
LukaHietala (Pix.#0001) - https://github.com/LukaHietala LukaHietala (Pix.#0001) - https://github.com/LukaHietala
Freebiell (Freebie#6429) - https://github.com/FreebieII

@ -37,7 +37,7 @@ class AWSPolly:
else: else:
if not settings.config["settings"]["tts"]["aws_polly_voice"]: if not settings.config["settings"]["tts"]["aws_polly_voice"]:
return ValueError( return ValueError(
f"Please set the environment variable AWS_VOICE to a valid voice. options are: {voices}" f"Please set the TOML variable AWS_VOICE to a valid voice. options are: {voices}"
) )
voice = str( voice = str(
settings.config["settings"]["tts"]["aws_polly_voice"] settings.config["settings"]["tts"]["aws_polly_voice"]

@ -37,7 +37,7 @@ class StreamlabsPolly:
else: else:
if not settings.config["settings"]["tts"]["streamlabs_polly_voice"]: if not settings.config["settings"]["tts"]["streamlabs_polly_voice"]:
return ValueError( return ValueError(
f"Please set the environment variable STREAMLABS_VOICE to a valid voice. options are: {voices}" f"Please set the config variable STREAMLABS_VOICE to a valid voice. options are: {voices}"
) )
voice = str( voice = str(
settings.config["settings"]["tts"]["streamlabs_polly_voice"] settings.config["settings"]["tts"]["streamlabs_polly_voice"]

@ -8,12 +8,12 @@ from utils.console import print_markdown, print_step
from utils import settings from utils import settings
# from utils.checker import envUpdate # from utils.checker import envUpdate
from video_creation.background import download_background, chop_background_video from video_creation.background import download_background, chop_background_video, get_background_config
from video_creation.final_video import make_final_video from video_creation.final_video import make_final_video
from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3 from video_creation.voices import save_text_to_mp3
VERSION = "2.2.2" VERSION = "2.2.9"
print( print(
""" """
@ -37,9 +37,10 @@ def main(POST_ID=None):
length, number_of_comments = save_text_to_mp3(reddit_object) length, number_of_comments = save_text_to_mp3(reddit_object)
length = math.ceil(length) length = math.ceil(length)
download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_screenshots_of_reddit_posts(reddit_object, number_of_comments)
download_background() bg_config = get_background_config()
credit = chop_background_video(length) download_background(bg_config)
make_final_video(number_of_comments, length, reddit_object, credit) chop_background_video(bg_config, length)
make_final_video(number_of_comments, length, reddit_object, bg_config)
def run_many(times): def run_many(times):

@ -28,7 +28,7 @@ def check_done(
if video["id"] == str(redditobj): if video["id"] == str(redditobj):
if settings.config["reddit"]["thread"]["post_id"]: if settings.config["reddit"]["thread"]["post_id"]:
print_step( 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 config file the program will continue"
) )
return redditobj 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")
@ -48,7 +48,7 @@ def save_data(filename: str, reddit_title: str, reddit_id: str, credit: str):
with open("./video_creation/data/videos.json", "r+", encoding="utf-8") as raw_vids: with open("./video_creation/data/videos.json", "r+", encoding="utf-8") as raw_vids:
done_vids = json.load(raw_vids) done_vids = json.load(raw_vids)
if reddit_id in [video["id"] for video in done_vids]: if reddit_id in [video["id"] for video in done_vids]:
return # video already done but was specified to continue anyway in the .env file return # video already done but was specified to continue anyway in the config file
payload = { payload = {
"id": reddit_id, "id": reddit_id,
"time": str(int(time.time())), "time": str(int(time.time())),

@ -1,15 +1,50 @@
import random
from os import listdir
from pathlib import Path from pathlib import Path
import random
from random import randrange from random import randrange
from typing import Tuple from typing import Any, Tuple
from moviepy.editor import VideoFileClip from moviepy.editor import VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from pytube import YouTube from pytube import YouTube
from pytube.cli import on_progress
from utils import settings
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
# Supported Background. Can add/remove background video here....
# <key>-<value> : key -> used as keyword for TOML file. value -> background configuration
# Format (value):
# 1. Youtube URI
# 2. filename
# 3. Citation (owner of the video)
# 4. Position of image clips in the background. See moviepy reference for more information. (https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#moviepy.video.VideoClip.VideoClip.set_position)
background_options = {
"motor-gta": ( # Motor-GTA Racing
"https://www.youtube.com/watch?v=vw5L4xCPy9Q",
"bike-parkour-gta.mp4",
"Achy Gaming",
lambda t: ('center', 480 + t)
),
"rocket-league": ( # Rocket League
"https://www.youtube.com/watch?v=2X9QGY__0II",
"rocket_league.mp4",
"Orbital Gameplay",
"top"
),
"minecraft": ( # Minecraft parkour
"https://www.youtube.com/watch?v=n_Dv4JMiwK8",
"parkour.mp4",
"bbswitzer",
"center"
),
"gta": ( # GTA Stunt Race
"https://www.youtube.com/watch?v=qGa9kWREOnE",
"gta-stunt-race.mp4",
"Achy Gaming",
lambda t: ('center', 480 + t)
)
}
def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]: def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]:
"""Generates a random interval of time to be used as the background of the video. """Generates a random interval of time to be used as the background of the video.
@ -24,52 +59,56 @@ def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int
random_time = randrange(180, int(length_of_clip) - int(video_length)) random_time = randrange(180, int(length_of_clip) - int(video_length))
return random_time, random_time + video_length return random_time, random_time + video_length
def get_background_config():
"""Fetch the background/s configuration"""
try:
choice = str(settings.config['settings']['background_choice']).casefold()
except AttributeError:
print_substep("No background selected. Picking random background'")
choice = None
# Handle default / not supported background using default option.
# Default : pick random from supported background.
if not choice or choice not in background_options:
choice = random.choice(list(background_options.keys()))
return background_options[choice]
def download_background():
"""Downloads the backgrounds/s video from YouTube.""" def download_background(background_config: Tuple[str, str, str, Any]):
"""Downloads the background/s video from YouTube."""
Path("./assets/backgrounds/").mkdir(parents=True, exist_ok=True) 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",
# ),
]
# note: make sure the file name doesn't include an - in it # note: make sure the file name doesn't include an - in it
if not len(listdir("./assets/backgrounds")) >= len( uri, filename, credit, _ = background_config
background_options if Path(f"assets/backgrounds/{credit}-{filename}").is_file():
): # if there are any background videos not installed return
print_step( print_step(
"We need to download the backgrounds videos. they are fairly large but it's only done once. 😎" "We need to download the backgrounds videos. they are fairly large but it's only done once. 😎"
) )
print_substep("Downloading the backgrounds videos... please be patient 🙏 ") print_substep("Downloading the backgrounds videos... please be patient 🙏 ")
for uri, filename, credit in background_options:
if Path(f"assets/backgrounds/{credit}-{filename}").is_file():
continue # adds check to see if file exists before downloading
print_substep(f"Downloading {filename} from {uri}") print_substep(f"Downloading {filename} from {uri}")
YouTube(uri).streams.filter(res="1080p").first().download( YouTube(uri, on_progress_callback=on_progress).streams.filter(res="1080p").first().download(
"assets/backgrounds", filename=f"{credit}-{filename}" "assets/backgrounds", filename=f"{credit}-{filename}"
) )
print_substep("Background videos downloaded successfully! 🎉",
print_substep( style="bold green")
"Background videos downloaded successfully! 🎉", style="bold green"
)
def chop_background_video(video_length: int) -> str: def chop_background_video(background_config: Tuple[str, str, str, Any], video_length: int):
"""Generates the background footage to be used in the video and writes it to assets/temp/background.mp4 """Generates the background footage to be used in the video and writes it to assets/temp/background.mp4
Args: Args:
background_config (Tuple[str, str, str, Any]) : Current background configuration
video_length (int): Length of the clip where the background footage is to be taken out of video_length (int): Length of the clip where the background footage is to be taken out of
""" """
print_step("Finding a spot in the backgrounds video to chop...✂️") print_step("Finding a spot in the backgrounds video to chop...✂️")
choice = random.choice(listdir("assets/backgrounds")) choice = f"{background_config[2]}-{background_config[1]}"
credit = choice.split("-")[0]
background = VideoFileClip(f"assets/backgrounds/{choice}") background = VideoFileClip(f"assets/backgrounds/{choice}")
start_time, end_time = get_start_and_end_times(video_length, background.duration) start_time, end_time = get_start_and_end_times(
video_length, background.duration)
try: try:
ffmpeg_extract_subclip( ffmpeg_extract_subclip(
f"assets/backgrounds/{choice}", f"assets/backgrounds/{choice}",
@ -83,4 +122,4 @@ def chop_background_video(video_length: int) -> str:
new = video.subclip(start_time, end_time) new = video.subclip(start_time, end_time)
new.write_videofile("assets/temp/background.mp4") new.write_videofile("assets/temp/background.mp4")
print_substep("Background video chopped successfully!", style="bold green") print_substep("Background video chopped successfully!", style="bold green")
return credit return background_config[2]

@ -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 Dict from typing import Dict, Tuple, Any
import translators as ts import translators as ts
from moviepy.editor import ( from moviepy.editor import (
@ -23,6 +23,7 @@ from utils.console import print_step, print_substep
from utils.videos import save_data from utils.videos import save_data
from utils import settings from utils import settings
console = Console() console = Console()
W, H = 1080, 1920 W, H = 1080, 1920
@ -32,7 +33,7 @@ def name_normalize(name: str) -> str:
name = re.sub(r'[?\\"%*:|<>]', "", name) name = re.sub(r'[?\\"%*:|<>]', "", name)
name = re.sub(r"( [w,W]\s?\/\s?[o,O,0])", r" without", name) name = re.sub(r"( [w,W]\s?\/\s?[o,O,0])", r" without", name)
name = re.sub(r"( [w,W]\s?\/)", r" with", name) name = re.sub(r"( [w,W]\s?\/)", r" with", name)
name = re.sub(r"([0-9]+)\s?\/\s?([0-9]+)", r"\1 of \2", name) name = re.sub(r"(\d+)\s?\/\s?(\d+)", r"\1 of \2", name)
name = re.sub(r"(\w+)\s?\/\s?(\w+)", r"\1 or \2", name) name = re.sub(r"(\w+)\s?\/\s?(\w+)", r"\1 or \2", name)
name = re.sub(r"\/", r"", name) name = re.sub(r"\/", r"", name)
@ -45,16 +46,13 @@ def name_normalize(name: str) -> str:
else: else:
return name return name
def make_final_video(number_of_clips: int, length: int, reddit_obj: dict, background_config: Tuple[str, str, str, Any]):
def make_final_video(
number_of_clips: int, length: int, reddit_obj: dict, background_credit: str
):
"""Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp """Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp
Args: Args:
number_of_clips (int): Index to end at when going through the screenshots number_of_clips (int): Index to end at when going through the screenshots'
length (int): Length of the video length (int): Length of the 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.
""" """
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)
@ -80,12 +78,10 @@ def make_final_video(
image_clips = [] image_clips = []
# 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)
image_clips.insert( image_clips.insert(
0, 0,
ImageClip("assets/temp/png/title.png") ImageClip("assets/temp/png/title.png")
.set_duration(audio_clips[0].duration) .set_duration(audio_clips[0].duration)
.set_position("center")
.resize(width=W - 100) .resize(width=W - 100)
.set_opacity(new_opacity), .set_opacity(new_opacity),
) )
@ -94,7 +90,6 @@ def make_final_video(
image_clips.append( image_clips.append(
ImageClip(f"assets/temp/png/comment_{i}.png") ImageClip(f"assets/temp/png/comment_{i}.png")
.set_duration(audio_clips[i + 1].duration) .set_duration(audio_clips[i + 1].duration)
.set_position("center")
.resize(width=W - 100) .resize(width=W - 100)
.set_opacity(new_opacity) .set_opacity(new_opacity)
) )
@ -108,10 +103,10 @@ def make_final_video(
# .resize(width=W - 100) # .resize(width=W - 100)
# .set_opacity(float(opacity)), # .set_opacity(float(opacity)),
# ) # )
# else: # else: story mode stuff
image_concat = concatenate_videoclips(image_clips).set_position( img_clip_pos = background_config[3]
("center", "center") image_concat = concatenate_videoclips(
) image_clips).set_position(img_clip_pos)
image_concat.audio = audio_composite image_concat.audio = audio_composite
final = CompositeVideoClip([background_clip, image_concat]) final = CompositeVideoClip([background_clip, image_concat])
title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"]) title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"])
@ -120,7 +115,7 @@ def make_final_video(
filename = f"{name_normalize(title)}.mp4" filename = f"{name_normalize(title)}.mp4"
subreddit = settings.config["reddit"]["thread"]["subreddit"] subreddit = settings.config["reddit"]["thread"]["subreddit"]
save_data(filename, title, idx, background_credit) save_data(filename, title, idx, background_config[2])
if not exists(f"./results/{subreddit}"): if not exists(f"./results/{subreddit}"):
print_substep("The results folder didn't exist so I made it") print_substep("The results folder didn't exist so I made it")
@ -148,5 +143,5 @@ def make_final_video(
print_substep("See result in the results folder!") print_substep("See result in the results folder!")
print_step( print_step(
f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_credit}' f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_config[2]}'
) )

Loading…
Cancel
Save