Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
elebumm
GitHub Repository: elebumm/RedditVideoMakerBot
Path: blob/master/video_creation/background.py
327 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.editor 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
background_config (Dict[str,Tuple]]) : Current background configuration
128
video_length (int): Length of the clip where the background footage is to be taken out of
129
"""
130
id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"])
131
132
if settings.config["settings"]["background"][f"background_audio_volume"] == 0:
133
print_step("Volume was set to 0. Skipping background audio creation . . .")
134
else:
135
print_step("Finding a spot in the backgrounds audio to chop...✂️")
136
audio_choice = f"{background_config['audio'][2]}-{background_config['audio'][1]}"
137
background_audio = AudioFileClip(f"assets/backgrounds/audio/{audio_choice}")
138
start_time_audio, end_time_audio = get_start_and_end_times(
139
video_length, background_audio.duration
140
)
141
background_audio = background_audio.subclip(start_time_audio, end_time_audio)
142
background_audio.write_audiofile(f"assets/temp/{id}/background.mp3")
143
144
print_step("Finding a spot in the backgrounds video to chop...✂️")
145
video_choice = f"{background_config['video'][2]}-{background_config['video'][1]}"
146
background_video = VideoFileClip(f"assets/backgrounds/video/{video_choice}")
147
start_time_video, end_time_video = get_start_and_end_times(
148
video_length, background_video.duration
149
)
150
# Extract video subclip
151
try:
152
ffmpeg_extract_subclip(
153
f"assets/backgrounds/video/{video_choice}",
154
start_time_video,
155
end_time_video,
156
targetname=f"assets/temp/{id}/background.mp4",
157
)
158
except (OSError, IOError): # ffmpeg issue see #348
159
print_substep("FFMPEG issue. Trying again...")
160
with VideoFileClip(f"assets/backgrounds/video/{video_choice}") as video:
161
new = video.subclip(start_time_video, end_time_video)
162
new.write_videofile(f"assets/temp/{id}/background.mp4")
163
print_substep("Background video chopped successfully!", style="bold green")
164
return background_config["video"][2]
165
166
167
# Create a tuple for downloads background (background_audio_options, background_video_options)
168
background_options = load_background_options()
169
170