The RedditVideoMakerBot has been updated to version 3.2, bringing exciting new features and bug fixes. Introducing **ElevenLabs TTS** for high-quality audio. Enjoy **background audio** and **random voices per comment**. Run the bot with one click using the **bat file**. Use the **zoom setting** for bigger text. Bug fixes include video downloader improvements, random story mode fix, updated dependencies, code optimizations, text size adjustments, enhanced Reddit credentials validation, and translator fixes. Enjoy the new features and **thanks to all contributors**!
174 lines
7.0 KiB
Python
174 lines
7.0 KiB
Python
import json
|
|
import random
|
|
import re
|
|
from pathlib import Path
|
|
from random import randrange
|
|
from typing import Any, Tuple, Dict
|
|
|
|
from moviepy.editor import VideoFileClip, AudioFileClip
|
|
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
|
|
from utils import settings
|
|
from utils.console import print_step, print_substep
|
|
import yt_dlp
|
|
|
|
|
|
def load_background_options():
|
|
background_options = {}
|
|
# Load background videos
|
|
with open("./utils/background_videos.json") as json_file:
|
|
background_options["video"] = json.load(json_file)
|
|
|
|
# Load background audios
|
|
with open("./utils/background_audios.json") as json_file:
|
|
background_options["audio"] = json.load(json_file)
|
|
|
|
# Remove "__comment" from backgrounds
|
|
del background_options["video"]["__comment"]
|
|
del background_options["audio"]["__comment"]
|
|
|
|
for name in list(background_options["video"].keys()):
|
|
pos = background_options["video"][name][3]
|
|
|
|
if pos != "center":
|
|
background_options["video"][name][3] = lambda t: ("center", pos + t)
|
|
|
|
return background_options
|
|
|
|
|
|
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.
|
|
|
|
Args:
|
|
video_length (int): Length of the video
|
|
length_of_clip (int): Length of the video to be used as the background
|
|
|
|
Returns:
|
|
tuple[int,int]: Start and end time of the randomized interval
|
|
"""
|
|
initialValue = 180
|
|
# Issue #1649 - Ensures that will be a valid interval in the video
|
|
while int(length_of_clip) <= int(video_length + initialValue):
|
|
if initialValue == initialValue // 2:
|
|
raise Exception("Your background is too short for this video length")
|
|
else:
|
|
initialValue //= 2 # Divides the initial value by 2 until reach 0
|
|
random_time = randrange(initialValue, int(length_of_clip) - int(video_length))
|
|
return random_time, random_time + video_length
|
|
|
|
|
|
def get_background_config(mode: str):
|
|
"""Fetch the background/s configuration"""
|
|
try:
|
|
choice = str(
|
|
settings.config["settings"]["background"][f"background_{mode}"]
|
|
).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[mode]:
|
|
choice = random.choice(list(background_options[mode].keys()))
|
|
|
|
return background_options[mode][choice]
|
|
|
|
|
|
def download_background_video(background_config: Tuple[str, str, str, Any]):
|
|
"""Downloads the background/s video from YouTube."""
|
|
Path("./assets/backgrounds/video/").mkdir(parents=True, exist_ok=True)
|
|
# note: make sure the file name doesn't include an - in it
|
|
uri, filename, credit, _ = background_config
|
|
if Path(f"assets/backgrounds/video/{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}")
|
|
ydl_opts = {
|
|
"format": "bestvideo[height<=1080][ext=mp4]",
|
|
"outtmpl": f"assets/backgrounds/video/{credit}-{filename}",
|
|
"retries": 10,
|
|
}
|
|
|
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
|
ydl.download(uri)
|
|
print_substep("Background video downloaded successfully! 🎉", style="bold green")
|
|
|
|
|
|
def download_background_audio(background_config: Tuple[str, str, str]):
|
|
"""Downloads the background/s audio from YouTube."""
|
|
Path("./assets/backgrounds/audio/").mkdir(parents=True, exist_ok=True)
|
|
# note: make sure the file name doesn't include an - in it
|
|
uri, filename, credit = background_config
|
|
if Path(f"assets/backgrounds/audio/{credit}-{filename}").is_file():
|
|
return
|
|
print_step(
|
|
"We need to download the backgrounds audio. they are fairly large but it's only done once. 😎"
|
|
)
|
|
print_substep("Downloading the backgrounds audio... please be patient 🙏 ")
|
|
print_substep(f"Downloading {filename} from {uri}")
|
|
ydl_opts = {
|
|
"outtmpl": f"./assets/backgrounds/audio/{credit}-{filename}",
|
|
"format": "bestaudio/best",
|
|
"extract_audio": True,
|
|
}
|
|
|
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
|
ydl.download([uri])
|
|
|
|
print_substep("Background audio downloaded successfully! 🎉", style="bold green")
|
|
|
|
|
|
def chop_background(
|
|
background_config: Dict[str, Tuple], video_length: int, reddit_object: dict
|
|
):
|
|
"""Generates the background audio and footage to be used in the video and writes it to assets/temp/background.mp3 and assets/temp/background.mp4
|
|
|
|
Args:
|
|
background_config (Dict[str,Tuple]]) : Current background configuration
|
|
video_length (int): Length of the clip where the background footage is to be taken out of
|
|
"""
|
|
id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"])
|
|
|
|
if settings.config["settings"]["background"][f"background_audio_volume"] == 0:
|
|
print_step("Volume was set to 0. Skipping background audio creation . . .")
|
|
else:
|
|
print_step("Finding a spot in the backgrounds audio to chop...✂️")
|
|
audio_choice = (
|
|
f"{background_config['audio'][2]}-{background_config['audio'][1]}"
|
|
)
|
|
background_audio = AudioFileClip(f"assets/backgrounds/audio/{audio_choice}")
|
|
start_time_audio, end_time_audio = get_start_and_end_times(
|
|
video_length, background_audio.duration
|
|
)
|
|
background_audio = background_audio.subclip(start_time_audio, end_time_audio)
|
|
background_audio.write_audiofile(f"assets/temp/{id}/background.mp3")
|
|
|
|
print_step("Finding a spot in the backgrounds video to chop...✂️")
|
|
video_choice = f"{background_config['video'][2]}-{background_config['video'][1]}"
|
|
background_video = VideoFileClip(f"assets/backgrounds/video/{video_choice}")
|
|
start_time_video, end_time_video = get_start_and_end_times(
|
|
video_length, background_video.duration
|
|
)
|
|
# Extract video subclip
|
|
try:
|
|
ffmpeg_extract_subclip(
|
|
f"assets/backgrounds/video/{video_choice}",
|
|
start_time_video,
|
|
end_time_video,
|
|
targetname=f"assets/temp/{id}/background.mp4",
|
|
)
|
|
except (OSError, IOError): # ffmpeg issue see #348
|
|
print_substep("FFMPEG issue. Trying again...")
|
|
with VideoFileClip(f"assets/backgrounds/video/{video_choice}") as video:
|
|
new = video.subclip(start_time_video, end_time_video)
|
|
new.write_videofile(f"assets/temp/{id}/background.mp4")
|
|
print_substep("Background video chopped successfully!", style="bold green")
|
|
return background_config["video"][2]
|
|
|
|
|
|
# Create a tuple for downloads background (background_audio_options, background_video_options)
|
|
background_options = load_background_options()
|