background audio implementation
This commit is contained in:
1 parent
a4149e1d12
commit
c412155720
9 files changed
+62
-90
No files matched your search
@@ -29,10 +29,15 @@ opacity = { optional = false, default = 0.9, example = 0.8, explanation = "Sets
|
||||
storymode = { optional = true, type = "bool", default = false, example = false, options = [true,
|
||||
false,
|
||||
], explanation = "not yet implemented" }
|
||||
|
||||
[settings.background]
|
||||
background_choice = { optional = true, default = "minecraft", example = "minecraft", options = ["minecraft", "gta", "rocket-league", "motor-gta", ""], explanation = "Sets the background for the video" }
|
||||
background_audio = { optional = true, type = "bool", default = false, example = false, options = [true,
|
||||
false,
|
||||
], explaination="Sets a audio to play in the background (put a background.mp3 file in the assets/backgrounds directory for it to be used.)" }
|
||||
background_audio_volume = { optional = true, type = "float", default = 0.3, example = 0.1, explanation="Sets the volume of the background audio. only used if the background_audio is also set to true" }
|
||||
|
||||
|
||||
[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." }
|
||||
aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" }
|
||||
|
||||
+10
-5
@@ -47,7 +47,7 @@ class TTSEngine:
|
||||
|
||||
Path(self.path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# This file needs to be removed in case this post does not use post text, so that it wont appear in the final video
|
||||
# This file needs to be removed in case this post does not use post text, so that it won't appear in the final video
|
||||
try:
|
||||
Path(f"{self.path}/posttext.mp3").unlink()
|
||||
except OSError:
|
||||
@@ -67,10 +67,12 @@ class TTSEngine:
|
||||
# ! Stop creating mp3 files if the length is greater than max length.
|
||||
if self.length > self.max_length:
|
||||
break
|
||||
if not self.tts_module.max_chars:
|
||||
if (
|
||||
len(comment["comment_body"]) > self.tts_module.max_chars
|
||||
): # Split the comment if it is too long
|
||||
self.split_post(comment["comment_body"], idx) # Split the comment
|
||||
else: # If the comment is not too long, just call the tts engine
|
||||
self.call_tts(f"{idx}", comment["comment_body"])
|
||||
else:
|
||||
self.split_post(comment["comment_body"], idx)
|
||||
|
||||
print_substep("Saved Text to MP3 files successfully.", style="bold green")
|
||||
return self.length, idx
|
||||
@@ -84,9 +86,12 @@ class TTSEngine:
|
||||
)
|
||||
]
|
||||
|
||||
idy = None
|
||||
for idy, text_cut in enumerate(split_text):
|
||||
# print(f"{idx}-{idy}: {text_cut}\n")
|
||||
if text_cut == "":
|
||||
print("Empty text cut: tell the devs about this")
|
||||
continue
|
||||
|
||||
self.call_tts(f"{idx}-{idy}.part", text_cut)
|
||||
split_files.append(AudioFileClip(f"{self.path}/{idx}-{idy}.part.mp3"))
|
||||
CompositeAudioClip([concatenate_audioclips(split_files)]).write_audiofile(
|
||||
|
||||
@@ -48,10 +48,12 @@ class StreamlabsPolly:
|
||||
|
||||
else:
|
||||
try:
|
||||
print(body)
|
||||
print(response.json())
|
||||
voice_data = requests.get(response.json()["speak_url"])
|
||||
with open(filepath, "wb") as f:
|
||||
f.write(voice_data.content)
|
||||
except (KeyError, JSONDecodeError):
|
||||
except (KeyError, JSONDecodeError) as e:
|
||||
try:
|
||||
if response.json()["error"] == "No text specified!":
|
||||
raise ValueError("Please specify a text to convert to speech.")
|
||||
|
||||
@@ -32,7 +32,7 @@ print(
|
||||
print_markdown(
|
||||
"### Thanks for using this tool! [Feel free to contribute to this project on GitHub!](https://lewismenelaws.com) If you have any questions, feel free to reach out to me on Twitter or submit a GitHub issue. You can find solutions to many common problems in the [Documentation](https://luka-hietala.gitbook.io/documentation-for-the-reddit-bot/)"
|
||||
)
|
||||
print_step(f"You are using V{VERSION} of the bot")
|
||||
print_step(f"You are using v{VERSION} of the bot")
|
||||
|
||||
|
||||
def main(POST_ID=None):
|
||||
|
||||
+5
-5
@@ -18,7 +18,7 @@ def get_subreddit_threads(POST_ID: str):
|
||||
print_substep("Logging into Reddit.")
|
||||
|
||||
content = {}
|
||||
if settings.config["reddit"]["creds"]["2fa"] == True:
|
||||
if settings.config["reddit"]["creds"]["2fa"]:
|
||||
print("\nEnter your two-factor authentication code from your authenticator app.\n")
|
||||
code = input("> ")
|
||||
print()
|
||||
@@ -27,7 +27,7 @@ def get_subreddit_threads(POST_ID: str):
|
||||
else:
|
||||
passkey = settings.config["reddit"]["creds"]["password"]
|
||||
username = settings.config["reddit"]["creds"]["username"]
|
||||
if username.casefold().startswith("u/"):
|
||||
if str(username).casefold().startswith("u/"):
|
||||
username = username[2:]
|
||||
reddit = praw.Reddit(
|
||||
client_id=settings.config["reddit"]["creds"]["client_id"],
|
||||
@@ -55,7 +55,7 @@ def get_subreddit_threads(POST_ID: str):
|
||||
sub = settings.config["reddit"]["thread"]["subreddit"]
|
||||
print_substep(f"Using subreddit: r/{sub} from TOML config")
|
||||
subreddit_choice = sub
|
||||
if subreddit_choice.casefold().startswith("r/"): # removes the r/ from the input
|
||||
if str(subreddit_choice).casefold().startswith("r/"): # removes the r/ from the input
|
||||
subreddit_choice = subreddit_choice[2:]
|
||||
subreddit = reddit.subreddit(
|
||||
subreddit_choice
|
||||
@@ -65,11 +65,10 @@ def get_subreddit_threads(POST_ID: str):
|
||||
submission = reddit.submission(id=POST_ID)
|
||||
elif (
|
||||
settings.config["reddit"]["thread"]["post_id"]
|
||||
and len(settings.config["reddit"]["thread"]["post_id"].split("+")) == 1
|
||||
and len(str(settings.config["reddit"]["thread"]["post_id"]).split("+")) == 1
|
||||
):
|
||||
submission = reddit.submission(id=settings.config["reddit"]["thread"]["post_id"])
|
||||
else:
|
||||
|
||||
threads = subreddit.hot(limit=25)
|
||||
submission = get_subreddit_undone(threads, subreddit)
|
||||
submission = check_done(submission) # double-checking
|
||||
@@ -104,6 +103,7 @@ def get_subreddit_threads(POST_ID: str):
|
||||
):
|
||||
if (
|
||||
top_level_comment.author is not None
|
||||
and sanitize_text(top_level_comment.body) is not None
|
||||
): # if errors occur with this change to if not.
|
||||
content["comments"].append(
|
||||
{
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ def sleep_until(time):
|
||||
|
||||
# Convert datetime to unix timestamp and adjust for locality
|
||||
if isinstance(time, datetime):
|
||||
# If we're on Python 3 and the user specified a timezone, convert to UTC and get tje timestamp.
|
||||
# If we're on Python 3 and the user specified a timezone, convert to UTC and get the timestamp.
|
||||
if sys.version_info[0] >= 3 and time.tzinfo:
|
||||
end = time.astimezone(timezone.utc).timestamp()
|
||||
else:
|
||||
|
||||
@@ -64,7 +64,7 @@ def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int
|
||||
def get_background_config():
|
||||
"""Fetch the background/s configuration"""
|
||||
try:
|
||||
choice = str(settings.config["settings"]["background_choice"]).casefold()
|
||||
choice = str(settings.config["settings"]["background"]["background_choice"]).casefold()
|
||||
except AttributeError:
|
||||
print_substep("No background selected. Picking random background'")
|
||||
choice = None
|
||||
|
||||
@@ -3,30 +3,22 @@ import multiprocessing
|
||||
import os
|
||||
import re
|
||||
from os.path import exists
|
||||
from typing import Dict, Tuple, Any
|
||||
|
||||
from typing import Tuple, Any
|
||||
import translators as ts
|
||||
|
||||
from moviepy.editor import (
|
||||
VideoFileClip,
|
||||
AudioFileClip,
|
||||
ImageClip,
|
||||
concatenate_videoclips,
|
||||
concatenate_audioclips,
|
||||
CompositeAudioClip,
|
||||
CompositeVideoClip,
|
||||
)
|
||||
from moviepy.editor import (VideoFileClip, AudioFileClip, ImageClip, concatenate_videoclips, concatenate_audioclips,
|
||||
CompositeAudioClip, CompositeVideoClip, )
|
||||
from moviepy.video.io.ffmpeg_tools import ffmpeg_merge_video_audio, ffmpeg_extract_subclip
|
||||
from rich.console import Console
|
||||
import moviepy.editor as mpe
|
||||
|
||||
from utils.cleanup import cleanup
|
||||
from utils.console import print_step, print_substep
|
||||
from utils.videos import save_data
|
||||
from utils import settings
|
||||
|
||||
|
||||
console = Console()
|
||||
|
||||
VOLUME_MULTIPLIER = settings.config["settings"]['background']["background_audio_volume"]
|
||||
W, H = 1080, 1920
|
||||
|
||||
|
||||
@@ -48,9 +40,7 @@ def name_normalize(name: str) -> str:
|
||||
return name
|
||||
|
||||
|
||||
def make_final_video(
|
||||
number_of_clips: int, length: int, reddit_obj: dict, background_config: Tuple[str, str, str, Any]
|
||||
):
|
||||
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'
|
||||
@@ -63,11 +53,8 @@ def make_final_video(
|
||||
VideoFileClip.reH = lambda clip: clip.resize(width=H)
|
||||
opacity = settings.config["settings"]["opacity"]
|
||||
background_clip = (
|
||||
VideoFileClip("assets/temp/background.mp4")
|
||||
.without_audio()
|
||||
.resize(height=H)
|
||||
.crop(x1=1166.6, y1=0, x2=2246.6, y2=1920)
|
||||
)
|
||||
VideoFileClip("assets/temp/background.mp4").without_audio().resize(height=H).crop(x1=1166.6, y1=0, x2=2246.6,
|
||||
y2=1920))
|
||||
|
||||
# Gather all audio clips
|
||||
audio_clips = [AudioFileClip(f"assets/temp/mp3/{i}.mp3") for i in range(number_of_clips)]
|
||||
@@ -80,21 +67,13 @@ 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)
|
||||
.resize(width=W - 100)
|
||||
.set_opacity(new_opacity),
|
||||
)
|
||||
image_clips.insert(0, ImageClip("assets/temp/png/title.png").set_duration(audio_clips[0].duration).resize(
|
||||
width=W - 100).set_opacity(new_opacity), )
|
||||
|
||||
for i in range(0, number_of_clips):
|
||||
image_clips.append(
|
||||
ImageClip(f"assets/temp/png/comment_{i}.png")
|
||||
.set_duration(audio_clips[i + 1].duration)
|
||||
.resize(width=W - 100)
|
||||
.set_opacity(new_opacity)
|
||||
)
|
||||
ImageClip(f"assets/temp/png/comment_{i}.png").set_duration(audio_clips[i + 1].duration).resize(
|
||||
width=W - 100).set_opacity(new_opacity))
|
||||
|
||||
# if os.path.exists("assets/mp3/posttext.mp3"):
|
||||
# image_clips.insert(
|
||||
@@ -122,51 +101,32 @@ def make_final_video(
|
||||
print_substep("The results folder didn't exist so I made it")
|
||||
os.makedirs(f"./results/{subreddit}")
|
||||
|
||||
final.write_videofile(
|
||||
"assets/temp/temp.mp4",
|
||||
fps=30,
|
||||
audio_codec="aac",
|
||||
audio_bitrate="192k",
|
||||
verbose=False,
|
||||
threads=multiprocessing.cpu_count(),
|
||||
)
|
||||
if settings.config["settings"]["background_audio"]:
|
||||
print("[bold green] Merging background audio with video")
|
||||
if not exists(f"assets/backgrounds/background.mp3"):
|
||||
print_substep(
|
||||
"Cannot find assets/backgrounds/background.mp3 audio file didn't so skipping."
|
||||
)
|
||||
ffmpeg_extract_subclip(
|
||||
"assets/temp/temp.mp4",
|
||||
0,
|
||||
final.duration,
|
||||
targetname=f"results/{subreddit}/{filename}",
|
||||
)
|
||||
else:
|
||||
ffmpeg_merge_video_audio(
|
||||
"assets/temp/temp.mp4",
|
||||
"assets/backgrounds/background.mp3",
|
||||
"assets/temp/temp_audio.mp4",
|
||||
)
|
||||
ffmpeg_extract_subclip( # check if this gets run
|
||||
"assets/temp/temp_audio.mp4",
|
||||
0,
|
||||
final.duration,
|
||||
targetname=f"results/{subreddit}/{filename}",
|
||||
)
|
||||
final.write_videofile("assets/temp/temp.mp4", fps=30, audio_codec="aac", audio_bitrate="192k", verbose=False,
|
||||
threads=multiprocessing.cpu_count(), )
|
||||
if settings.config["settings"]['background']["background_audio"] and exists(f"assets/backgrounds/background.mp3"):
|
||||
if not isinstance(VOLUME_MULTIPLIER, float):
|
||||
print("No background audio volume set, using default of .3 set it in the config.toml file")
|
||||
assert VOLUME_MULTIPLIER == float(0.3)
|
||||
print('Merging background audio with video')
|
||||
my_clip = mpe.VideoFileClip('assets/temp/temp.mp4')
|
||||
audio_background = AudioFileClip("assets/backgrounds/background.mp3")
|
||||
lowered_audio = audio_background.multiply_volume(
|
||||
VOLUME_MULTIPLIER) # lower volume by background_audio_volume, use with fx
|
||||
lowered_audio = lowered_audio.subclip(0, my_clip.duration) # trim the audio to the length of the video
|
||||
lowered_audio.set_duration(my_clip.duration) # set the duration of the audio to the length of the video
|
||||
final_audio = mpe.CompositeAudioClip([my_clip.audio, lowered_audio])
|
||||
final_clip = my_clip.set_audio(final_audio)
|
||||
|
||||
final_clip.write_videofile("assets/temp/temp_audio.mp4", fps=30, audio_codec="aac", audio_bitrate="192k",
|
||||
verbose=False, threads=multiprocessing.cpu_count())
|
||||
ffmpeg_extract_subclip( # check if this gets run
|
||||
"assets/temp/temp_audio.mp4", 0, final.duration, targetname=f"results/{subreddit}/{filename}", )
|
||||
else:
|
||||
print("debug duck")
|
||||
ffmpeg_extract_subclip(
|
||||
"assets/temp/temp.mp4",
|
||||
0,
|
||||
final.duration,
|
||||
targetname=f"results/{subreddit}/{filename}",
|
||||
)
|
||||
ffmpeg_extract_subclip("assets/temp/temp.mp4", 0, final.duration,
|
||||
targetname=f"results/{subreddit}/{filename}", )
|
||||
print_step("Removing temporary files 🗑")
|
||||
cleanups = cleanup()
|
||||
print_substep(f"Removed {cleanups} temporary files 🗑")
|
||||
print_substep("See result in the results folder!")
|
||||
|
||||
print_step(
|
||||
f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_config[2]}'
|
||||
)
|
||||
print_step(f'Reddit title: {reddit_obj["thread_title"]} \n Background Credit: {background_config[2]}')
|
||||
@@ -34,7 +34,7 @@ def save_text_to_mp3(reddit_obj) -> Tuple[int, int]:
|
||||
"""
|
||||
|
||||
voice = settings.config["settings"]["tts"]["choice"]
|
||||
if voice.casefold() in map(lambda _: _.casefold(), TTSProviders):
|
||||
if str(voice).casefold() in map(lambda _: _.casefold(), TTSProviders):
|
||||
text_to_mp3 = TTSEngine(get_case_insensitive_key_value(TTSProviders, voice), reddit_obj)
|
||||
else:
|
||||
while True:
|
||||
|
||||
Reference in new issue
Block a user