Merge pull request #879 from elebumm/feat/RedditPostPosition
Feat/reddit post position
This commit is contained in:
8 files changed
+112
-84
No files matched your search
+13
-18
@@ -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,
|
||||
false,
|
||||
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,
|
||||
false,
|
||||
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,
|
||||
false,
|
||||
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",
|
||||
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,
|
||||
false,
|
||||
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
|
||||
+1
-1
@@ -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):
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
def download_background():
|
||||
"""Downloads the backgrounds/s video from YouTube."""
|
||||
# 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(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
|
||||
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(
|
||||
"assets/backgrounds", filename=f"{credit}-{filename}"
|
||||
)
|
||||
|
||||
print_substep(
|
||||
"Background videos downloaded successfully! 🎉", style="bold green"
|
||||
)
|
||||
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 🙏 ")
|
||||
print_substep(f"Downloading {filename} from {uri}")
|
||||
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")
|
||||
|
||||
|
||||
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]}'
|
||||
)
|
||||
Reference in new issue
Block a user