Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
elebumm
GitHub Repository: elebumm/RedditVideoMakerBot
Path: blob/master/video_creation/background.py
493 views
1
import json
2
import random
3
import re
4
from pathlib import Path
5
from random import randrange
6
from typing import Any, Dict, Tuple
7
8
import yt_dlp
9
from moviepy import AudioFileClip, VideoFileClip
10
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
11
12
from utils import settings
13
from utils.console import print_step, print_substep
14
15
16
def load_background_options():
17
_background_options = {}
18
# Load background videos
19
with open("./utils/background_videos.json") as json_file:
20
_background_options["video"] = json.load(json_file)
21
22
# Load background audios
23
with open("./utils/background_audios.json") as json_file:
24
_background_options["audio"] = json.load(json_file)
25
26
# Remove "__comment" from backgrounds
27
del _background_options["video"]["__comment"]
28
del _background_options["audio"]["__comment"]
29
30
for name in list(_background_options["video"].keys()):
31
pos = _background_options["video"][name][3]
32
33
if pos != "center":
34
_background_options["video"][name][3] = lambda t: ("center", pos + t)
35
36
return _background_options
37
38
39
def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]:
40
"""Generates a random interval of time to be used as the background of the video.
41
42
Args:
43
video_length (int): Length of the video
44
length_of_clip (int): Length of the video to be used as the background
45
46
Returns:
47
tuple[int,int]: Start and end time of the randomized interval
48
"""
49
initialValue = 180
50
# Issue #1649 - Ensures that will be a valid interval in the video
51
while int(length_of_clip) <= int(video_length + initialValue):
52
if initialValue == initialValue // 2:
53
raise Exception("Your background is too short for this video length")
54
else:
55
initialValue //= 2 # Divides the initial value by 2 until reach 0
56
random_time = randrange(initialValue, int(length_of_clip) - int(video_length))
57
return random_time, random_time + video_length
58
59
60
def get_background_config(mode: str):
61
"""Fetch the background/s configuration"""
62
try:
63
choice = str(settings.config["settings"]["background"][f"background_{mode}"]).casefold()
64
except AttributeError:
65
print_substep("No background selected. Picking random background'")
66
choice = None
67
68
# Handle default / not supported background using default option.
69
# Default : pick random from supported background.
70
if not choice or choice not in background_options[mode]:
71
choice = random.choice(list(background_options[mode].keys()))
72
73
return background_options[mode][choice]
74
75
76
def download_background_video(background_config: Tuple[str, str, str, Any]):
77
"""Downloads the background/s video from YouTube."""
78
Path("./assets/backgrounds/video/").mkdir(parents=True, exist_ok=True)
79
# note: make sure the file name doesn't include an - in it
80
uri, filename, credit, _ = background_config
81
if Path(f"assets/backgrounds/video/{credit}-{filename}").is_file():
82
return
83
print_step(
84
"We need to download the backgrounds videos. they are fairly large but it's only done once. 😎"
85
)
86
print_substep("Downloading the backgrounds videos... please be patient 🙏 ")
87
print_substep(f"Downloading {filename} from {uri}")
88
ydl_opts = {
89
"format": "bestvideo[height<=1080][ext=mp4]",
90
"outtmpl": f"assets/backgrounds/video/{credit}-{filename}",
91
"retries": 10,
92
}
93
94
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
95
ydl.download(uri)
96
print_substep("Background video downloaded successfully! 🎉", style="bold green")
97
98
99
def download_background_audio(background_config: Tuple[str, str, str]):
100
"""Downloads the background/s audio from YouTube."""
101
Path("./assets/backgrounds/audio/").mkdir(parents=True, exist_ok=True)
102
# note: make sure the file name doesn't include an - in it
103
uri, filename, credit = background_config
104
if Path(f"assets/backgrounds/audio/{credit}-{filename}").is_file():
105
return
106
print_step(
107
"We need to download the backgrounds audio. they are fairly large but it's only done once. 😎"
108
)
109
print_substep("Downloading the backgrounds audio... please be patient 🙏 ")
110
print_substep(f"Downloading {filename} from {uri}")
111
ydl_opts = {
112
"outtmpl": f"./assets/backgrounds/audio/{credit}-{filename}",
113
"format": "bestaudio/best",
114
"extract_audio": True,
115
}
116
117
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
118
ydl.download([uri])
119
120
print_substep("Background audio downloaded successfully! 🎉", style="bold green")
121
122
123
def chop_background(background_config: Dict[str, Tuple], video_length: int, reddit_object: dict):
124
"""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
125
126
Args:
127
reddit_object (Dict[str,str]) : Reddit object
128
background_config (Dict[str,Tuple]]) : Current background configuration
129
video_length (int): Length of the clip where the background footage is to be taken out of
130
"""
131
thread_id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"])
132
133
if settings.config["settings"]["background"][f"background_audio_volume"] == 0:
134
print_step("Volume was set to 0. Skipping background audio creation . . .")
135
else:
136
print_step("Finding a spot in the backgrounds audio to chop...✂️")
137
audio_choice = f"{background_config['audio'][2]}-{background_config['audio'][1]}"
138
background_audio = AudioFileClip(f"assets/backgrounds/audio/{audio_choice}")
139
start_time_audio, end_time_audio = get_start_and_end_times(
140
video_length, background_audio.duration
141
)
142
background_audio = background_audio.subclipped(start_time_audio, end_time_audio)
143
background_audio.write_audiofile(f"assets/temp/{thread_id}/background.mp3")
144
145
print_step("Finding a spot in the backgrounds video to chop...✂️")
146
video_choice = f"{background_config['video'][2]}-{background_config['video'][1]}"
147
background_video = VideoFileClip(f"assets/backgrounds/video/{video_choice}")
148
start_time_video, end_time_video = get_start_and_end_times(
149
video_length, background_video.duration
150
)
151
# Extract video subclip
152
try:
153
with VideoFileClip(f"assets/backgrounds/video/{video_choice}") as video:
154
new = video.subclipped(start_time_video, end_time_video)
155
new.write_videofile(f"assets/temp/{thread_id}/background.mp4")
156
157
except (OSError, IOError): # ffmpeg issue see #348
158
print_substep("FFMPEG issue. Trying again...")
159
ffmpeg_extract_subclip(
160
f"assets/backgrounds/video/{video_choice}",
161
start_time_video,
162
end_time_video,
163
outputfile=f"assets/temp/{thread_id}/background.mp4",
164
)
165
print_substep("Background video chopped successfully!", style="bold green")
166
return background_config["video"][2]
167
168
169
# Create a tuple for downloads background (background_audio_options, background_video_options)
170
background_options = load_background_options()
171
172