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,41 +3,36 @@ 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." }
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" }
2fa = { optional = true, type = "bool", options = [
true,
2fa = { optional = true, type = "bool", options = [true,
false,
], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true }
[reddit.thread]
random = { optional = true, options = [
true,
random = { optional = true, options = [true,
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" }
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" }
post_id = { optional = true, default = "", regex = "^((?!://|://)[+a-zA-Z])*$", 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" }
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]
allow_nsfw = { optional = false, type = "bool", default = false, example = false, options = [
true,
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",
theme = { optional = false, default = "dark", example = "light", options = ["dark",
"light",
], 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." }
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 = [
true,
storymode = { optional = true, type = "bool", default = false, example = false, options = [true,
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]
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." }
aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" }
streamlabs_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for Streamlabs Polly" }
tiktok_voice = { optional = false, default = "en_us_006", example = "en_us_006", explanation = "The voice used for TikTok TTS" }

@ -75,12 +75,10 @@ Please read our [contributing guidelines](CONTRIBUTING.md) for more detailed inf
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
HarryDaDev (hrvyy#9677) - https://github.com/ImmaHarry
Verq (Verq#2338) - https://github.com/CordlessCoder
LukaHietala (Pix.#0001) - https://github.com/LukaHietala
Freebiell (Freebie#6429) - https://github.com/FreebieII

@ -37,7 +37,7 @@ class AWSPolly:
else:
if not settings.config["settings"]["tts"]["aws_polly_voice"]:
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(
settings.config["settings"]["tts"]["aws_polly_voice"]

@ -37,7 +37,7 @@ class StreamlabsPolly:
else:
if not settings.config["settings"]["tts"]["streamlabs_polly_voice"]:
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(
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.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.screenshot_downloader import download_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3
VERSION = "2.2.2"
VERSION = "2.2.9"
print(
"""
@ -37,9 +37,10 @@ def main(POST_ID=None):
length, number_of_comments = save_text_to_mp3(reddit_object)
length = math.ceil(length)
download_screenshots_of_reddit_posts(reddit_object, number_of_comments)
download_background()
credit = chop_background_video(length)
make_final_video(number_of_comments, length, reddit_object, credit)
bg_config = get_background_config()
download_background(bg_config)
chop_background_video(bg_config, length)
make_final_video(number_of_comments, length, reddit_object, bg_config)
def run_many(times):

@ -28,7 +28,7 @@ def check_done(
if video["id"] == str(redditobj):
if settings.config["reddit"]["thread"]["post_id"]:
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
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:
done_vids = json.load(raw_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 = {
"id": reddit_id,
"time": str(int(time.time())),

@ -1,15 +1,50 @@
import random
from os import listdir
from pathlib import Path
import random
from random import randrange
from typing import Tuple
from typing import Any, Tuple
from moviepy.editor import VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from pytube import YouTube
from pytube.cli import on_progress
from utils import settings
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]:
"""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))
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)
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
if not len(listdir("./assets/backgrounds")) >= len(
background_options
): # if there are any background videos not installed
uri, filename, credit, _ = background_config
if Path(f"assets/backgrounds/{credit}-{filename}").is_file():
return
print_step(
"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 🙏 ")
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}")
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}"
)
print_substep(
"Background videos downloaded successfully! 🎉", style="bold green"
)
print_substep("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
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
"""
print_step("Finding a spot in the backgrounds video to chop...✂️")
choice = random.choice(listdir("assets/backgrounds"))
credit = choice.split("-")[0]
choice = f"{background_config[2]}-{background_config[1]}"
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:
ffmpeg_extract_subclip(
f"assets/backgrounds/{choice}",
@ -83,4 +122,4 @@ def chop_background_video(video_length: int) -> str:
new = video.subclip(start_time, end_time)
new.write_videofile("assets/temp/background.mp4")
print_substep("Background video chopped successfully!", style="bold green")
return credit
return background_config[2]

@ -3,7 +3,7 @@ import multiprocessing
import os
import re
from os.path import exists
from typing import Dict
from typing import Dict, Tuple, Any
import translators as ts
from moviepy.editor import (
@ -23,6 +23,7 @@ from utils.console import print_step, print_substep
from utils.videos import save_data
from utils import settings
console = Console()
W, H = 1080, 1920
@ -32,7 +33,7 @@ def name_normalize(name: str) -> str:
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?\/)", 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"\/", r"", name)
@ -45,16 +46,13 @@ def name_normalize(name: str) -> str:
else:
return name
def make_final_video(
number_of_clips: int, length: int, reddit_obj: dict, background_credit: str
):
def make_final_video(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:
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
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 🎥")
VideoFileClip.reW = lambda clip: clip.resize(width=W)
@ -80,12 +78,10 @@ def make_final_video(
image_clips = []
# Gather all images
new_opacity = 1 if opacity is None or float(opacity) >= 1 else float(opacity)
image_clips.insert(
0,
ImageClip("assets/temp/png/title.png")
.set_duration(audio_clips[0].duration)
.set_position("center")
.resize(width=W - 100)
.set_opacity(new_opacity),
)
@ -94,7 +90,6 @@ def make_final_video(
image_clips.append(
ImageClip(f"assets/temp/png/comment_{i}.png")
.set_duration(audio_clips[i + 1].duration)
.set_position("center")
.resize(width=W - 100)
.set_opacity(new_opacity)
)
@ -108,10 +103,10 @@ def make_final_video(
# .resize(width=W - 100)
# .set_opacity(float(opacity)),
# )
# else:
image_concat = concatenate_videoclips(image_clips).set_position(
("center", "center")
)
# else: story mode stuff
img_clip_pos = background_config[3]
image_concat = concatenate_videoclips(
image_clips).set_position(img_clip_pos)
image_concat.audio = audio_composite
final = CompositeVideoClip([background_clip, image_concat])
title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"])
@ -120,7 +115,7 @@ def make_final_video(
filename = f"{name_normalize(title)}.mp4"
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}"):
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_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