diff --git a/README.md b/README.md index e6f61a6..81e37be 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ In its current state, this bot does exactly what it needs to do. However, improv I have tried to simplify the code so anyone can read it and start contributing at any skill level. Don't be shy :) contribute! - [ ] Creating better documentation and adding a command line interface. -- [ ] Allowing the user to choose background music for their videos. +- [x] Allowing the user to choose background music for their videos. - [x] Allowing users to choose a reddit thread instead of being randomized. - [x] Allowing users to choose a background that is picked instead of the Minecraft one. - [x] Allowing users to choose between any subreddit. diff --git a/requirements.txt b/requirements.txt index 22d3d7f..634a24b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,4 +19,5 @@ unidecode==1.3.2 spacy==3.4.1 torch==1.12.1 transformers==4.25.1 -ffmpeg-python==0.2.0 \ No newline at end of file +ffmpeg-python==0.2.0 +yt-dlp==2023.3.4 \ No newline at end of file diff --git a/utils/.config.template.toml b/utils/.config.template.toml index b4074d7..a9d6d27 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -35,7 +35,8 @@ resolution_h = { optional = false, default = 1920, example = 2560, explantation [settings.background] background_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck", "minecraft-2","multiversus","fall-guys","steep", ""], explanation = "Sets the background for the video based on game name" } background_audio = { optional = true, default = "lofi", example = "minecraft", options = ["minecraft","lofi","undertale"], explanation = "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.1, example = 0.05, explanation="Sets the volume of the background audio. Warning: if you set more than 1.0, the volume will be increased" } +background_audio_volume = { optional = true, type = "float", nmin = 0, nmax = 1, default = 0.1, example = 0.05, explanation="Sets the volume of the background audio. Type a value between 0 and 1.", oob_error = "The volume HAS to be between 0 and 1", input_error = "The volume HAS to be a float number between 0 and 1"} +allow_only_tts = { optional = true, type = "bool", default = false, example = false, explanation="Used if you want to render another video with only TTS audio in a separate folder", input_error = "The value HAS to be true or false"} background_thumbnail = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Generate a thumbnail for the video (put a thumbnail.png file in the assets/backgrounds directory.)" } background_thumbnail_font_family = { optional = true, default = "arial", example = "arial", explanation = "Font family for the thumbnail text" } background_thumbnail_font_size = { optional = true, type = "int", default = 96, example = 96, explanation = "Font size in pixels for the thumbnail text" } diff --git a/video_creation/background.py b/video_creation/background.py index 36400b2..3b896d9 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -5,12 +5,13 @@ from pathlib import Path from random import randrange from typing import Any, Tuple,Dict -from moviepy.editor import VideoFileClip,AudioFileClip,afx +from moviepy.editor import VideoFileClip,AudioFileClip 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 +import yt_dlp def load_background_options(): background_options = {} @@ -81,9 +82,14 @@ def download_background_video(background_config: Tuple[str, str, str, Any]): ) 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/video", filename=f"{credit}-{filename}") + 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]): @@ -103,6 +109,8 @@ def download_background_audio(background_config: Tuple[str, str, str]): ).first().download("assets/backgrounds/audio", filename=f"{credit}-{filename}") print_substep("Background audio downloaded successfully! 🎉", style="bold green") + + def chop_background( background_config: Dict[str,Tuple], video_length: int, reddit_object: dict ): @@ -112,23 +120,23 @@ def chop_background( 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]}" - audio_choice = f"{background_config['audio'][2]}-{background_config['audio'][1]}" - id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"]) background_video = VideoFileClip(f"assets/backgrounds/video/{video_choice}") - background_audio = AudioFileClip(f"assets/backgrounds/audio/{audio_choice}") start_time_video, end_time_video = get_start_and_end_times(video_length, background_video.duration) - start_time_audio, end_time_audio = get_start_and_end_times(video_length, background_audio.duration) - # Create a audio clip - background_audio_volume = settings.config["settings"]["background"][f"background_audio_volume"] - background_audio = background_audio.fx(afx.volumex,background_audio_volume) - background_audio = background_audio.subclip(start_time_audio,end_time_audio) - background_audio.write_audiofile(f"assets/temp/{id}/background.mp3") - #background_video.set_audio(background_audio) - - + # Extract video subclip try: ffmpeg_extract_subclip( f"assets/backgrounds/video/{video_choice}", diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 89187ca..63c5842 100644 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -103,6 +103,28 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str: exit() return output_path +def merge_background_audio(audio: ffmpeg, reddit_id: str): + """Gather an audio and merge with assets/backgrounds/background.mp3 + Args: + audio (ffmpeg): The TTS final audio but without background. + reddit_id (str): The ID of subreddit + """ + background_audio_volume = settings.config["settings"]["background"]["background_audio_volume"] + if (background_audio_volume == 0): + return audio # Return the original audio + else: + # sets volume to config + bg_audio = ( + ffmpeg.input(f"assets/temp/{reddit_id}/background.mp3") + .filter( + "volume", + background_audio_volume, + ) + ) + # Merges audio and background_audio + merged_audio = ffmpeg.filter([audio, bg_audio], "amix", duration="longest") + return merged_audio # Return merged audio + def make_final_video( number_of_clips: int, @@ -122,6 +144,10 @@ def make_final_video( H: Final[int] = int(settings.config["settings"]["resolution_h"]) reddit_id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) + + allowOnlyTTSFolder: bool = settings.config["settings"]["background"]["allow_only_tts"] \ + and settings.config["settings"]["background"]["background_audio_volume"] != 0 + print_step("Creating the final video 🎥") background_clip = ffmpeg.input(prepare_background(reddit_id, W=W, H=H)) @@ -177,8 +203,7 @@ def make_final_video( screenshot_width = int((W * 45) // 100) audio = ffmpeg.input(f"assets/temp/{reddit_id}/audio.mp3") - background_audio = ffmpeg.input(f"assets/temp/{reddit_id}/background.mp3") - final_audio = ffmpeg.filter([audio, background_audio], "amix") + final_audio = merge_background_audio(audio,reddit_id) image_clips = list() @@ -263,9 +288,9 @@ def make_final_video( print_substep("The results folder didn't exist so I made it") os.makedirs(f"./results/{subreddit}") - if not exists(f"./results/{subreddit}/{filename}"): - print_substep("The results folder didn't exist so I made it") - os.makedirs(f"./results/{subreddit}/{filename}") + if not exists(f"./results/{subreddit}/OnlyTTS") and allowOnlyTTSFolder: + print_substep("The 'OnlyTTS' folder didn't exist so I made it") + os.makedirs(f"./results/{subreddit}/OnlyTTS") # create a thumbnail for the video settingsbackground = settings.config["settings"]["background"] @@ -326,9 +351,10 @@ def make_final_video( old_percentage = pbar.n pbar.update(status - old_percentage) - path = f"results/{subreddit}/{filename}" - path = path[:251] + path = f"results/{subreddit}" #path = path + ".mp4" + if(allowOnlyTTSFolder): + processingLength = 2*length with ProgressFfmpeg(length, on_update_example) as progress: ffmpeg.output( @@ -348,32 +374,38 @@ def make_final_video( capture_stdout=False, capture_stderr=False, ) - - with ProgressFfmpeg(length, on_update_example) as progress: - ffmpeg.output( - background_clip, - audio, - path+"/NoBackgroundAudio.mp4", - f="mp4", - **{ - "c:v": "h264", - "b:v": "20M", - "b:a": "192k", - "threads": multiprocessing.cpu_count(), - }, - ).overwrite_output().global_args("-progress", progress.output_file.name).run( - quiet=True, - overwrite_output=True, - capture_stdout=False, - capture_stderr=False, - ) - old_percentage = pbar.n pbar.update(100 - old_percentage) + if(allowOnlyTTSFolder): + print_step("Rendering the Only TTS Video 🎥") + with ProgressFfmpeg(length, on_update_example) as progress: + ffmpeg.output( + background_clip, + audio, + path+f"/OnlyTTS/{filename}.mp4", + f="mp4", + **{ + "c:v": "h264", + "b:v": "20M", + "b:a": "192k", + "threads": multiprocessing.cpu_count(), + }, + ).overwrite_output().global_args("-progress", progress.output_file.name).run( + quiet=True, + overwrite_output=True, + capture_stdout=False, + capture_stderr=False, + ) + old_percentage = pbar.n + pbar.update(100 - old_percentage) + pbar.close() + + + save_data(subreddit, filename + ".mp4", title, idx, background_config['video'][2]) print_step("Removing temporary files 🗑") cleanups = cleanup(reddit_id) print_substep(f"Removed {cleanups} temporary files 🗑") - print_step("Done! 🎉 The video is in the results folder 📁") + print_step("Done! 🎉 The video is in the results folder 📁") \ No newline at end of file