diff --git a/.gitignore b/.gitignore index 41bdd5e..e428766 100644 --- a/.gitignore +++ b/.gitignore @@ -239,6 +239,7 @@ out results/* reddit-bot-351418-5560ebc49cac.json /.idea +/test *.pyc video_creation/data/videos.json video_creation/data/envvars.txt diff --git a/main.py b/main.py index efaa51c..2af1daa 100755 --- a/main.py +++ b/main.py @@ -1,5 +1,7 @@ #!/usr/bin/env python import math +import time +import schedule import sys from os import name from pathlib import Path @@ -50,6 +52,7 @@ def main(POST_ID=None) -> None: redditid = id(reddit_object) length, number_of_comments = save_text_to_mp3(reddit_object) length = math.ceil(length) + # length, number_of_comments = 360, 43 get_screenshots_of_reddit_posts(reddit_object, number_of_comments) bg_config = { "video": get_background_config("video"), @@ -78,6 +81,26 @@ def shutdown() -> NoReturn: print("Exiting...") sys.exit() +def run(): + if config["reddit"]["thread"]["post_id"]: + for index, post_id in enumerate(config["reddit"]["thread"]["post_id"].split("+")): + index += 1 + print_step( + f'on the {index}{("st" if index % 10 == 1 else ("nd" if index % 10 == 2 else ("rd" if index % 10 == 3 else "th")))} post of {len(config["reddit"]["thread"]["post_id"].split("+"))}' + ) + main(post_id) + Popen("cls" if name == "nt" else "clear", shell=True).wait() + elif config["settings"]["times_to_run"]: + run_many(config["settings"]["times_to_run"]) + else: + main() + + print_substep("The video was created successfully! 🎉", style="bold green") + print_substep( + f'Next run will be in {settings.config["settings"]["run_every"]} hours.', + style="bold green" + ) + if __name__ == "__main__": if sys.version_info.major != 3 or sys.version_info.minor != 10: @@ -102,18 +125,11 @@ if __name__ == "__main__": ) sys.exit() try: - if config["reddit"]["thread"]["post_id"]: - for index, post_id in enumerate(config["reddit"]["thread"]["post_id"].split("+")): - index += 1 - print_step( - f'on the {index}{("st" if index % 10 == 1 else ("nd" if index % 10 == 2 else ("rd" if index % 10 == 3 else "th")))} post of {len(config["reddit"]["thread"]["post_id"].split("+"))}' - ) - main(post_id) - Popen("cls" if name == "nt" else "clear", shell=True).wait() - elif config["settings"]["times_to_run"]: - run_many(config["settings"]["times_to_run"]) - else: - main() + run() + schedule.every(settings.config["settings"]["run_every"]).hours.do(run) + while True: + schedule.run_pending() + time.sleep(1) except KeyboardInterrupt: shutdown() except ResponseException: diff --git a/requirements.txt b/requirements.txt index c9abc85..9605669 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/utils/.config.template.toml b/utils/.config.template.toml index 32854ed..6052fef 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -31,9 +31,10 @@ storymode_max_length = { optional = true, default = 1000, example = 1000, explan resolution_w = { optional = false, default = 1080, example = 1440, explantation = "Sets the width in pixels of the final video" } resolution_h = { optional = false, default = 1920, example = 2560, explantation = "Sets the height in pixels of the final video" } zoom = { optional = true, default = 1, example = 1.1, explanation = "Sets the browser zoom level. Useful if you want the text larger.", type = "float", nmin = 0.1, nmax = 2, oob_error = "The text is really difficult to read at a zoom level higher than 2" } +run_every = { optional = false, default = 24, example = 5, explanation = "How often should the bot create a video (in hours).", type = "int", nmin = 1, nmax = 48, oob_error = "Please choose a number between 1 and 24." } [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_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["mudrunner", "granny-remake", ""], explanation = "Sets the background for the video based on game name" } background_audio = { optional = true, default = "lofi", example = "chill-summer", options = ["lofi","lofi-2","chill-summer", "eerie",""], explanation = "Sets the background audio for the video" } background_audio_volume = { optional = true, type = "float", nmin = 0, nmax = 1, default = 0.15, example = 0.05, explanation="Sets the volume of the background audio. If you don't want background audio, set it to 0.", 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"} enable_extra_audio = { optional = true, type = "bool", default = false, example = false, explanation="Used if you want to render another video without background audio in a separate folder", input_error = "The value HAS to be true or false"} @@ -49,8 +50,8 @@ elevenlabs_voice_name = { optional = false, default = "Bella", example = "Bella" elevenlabs_api_key = { optional = true, example = "21f13f91f54d741e2ae27d2ab1b99d59", explanation = "Elevenlabs API key" } unreal_speech_api_key = { optional = true, example = "21f13f91f54d741e2ae27d2ab1b99d59", explanation = "Unreal Speech API key" } unreal_speech_voice_name = { optional = false, default = "Liv", example = "Liv", explanation = "The voice used for Unreal Speech", options = ["Scarlett", "Amy", "Liv", "Dan", "Will", ] } -unreal_speech_voice_pitch = { optional = false, default = "1", example = "1.2", explanation = "The pitch of the voice used for Unreal Speech (0.5 to 1.5)", type = "float" } -unreal_speech_voice_speed = { optional = false, default = "0", example = "-0.15", explanation = "The speed of the voice used for Unreal Speech (-1.0 to 1.0)", type = "float" } +unreal_speech_voice_pitch = { optional = false, default = "1", example = "1.2", nmin = 0.5, nmax = 1.5, explanation = "The pitch of the voice used for Unreal Speech (0.5 to 1.5)", type = "float" } +unreal_speech_voice_speed = { optional = false, default = "0", example = "-0.15", nmin = -1.0, nmax = 1.0, explanation = "The speed of the voice used for Unreal Speech (-1.0 to 1.0)", type = "float" } aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" } streamlabs_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for Streamlabs Polly" } tiktok_voice = { optional = true, default = "en_us_001", example = "en_us_006", explanation = "The voice used for TikTok TTS" } diff --git a/utils/background_audios.json b/utils/background_audios.json index 2b21dc8..8cc352e 100644 --- a/utils/background_audios.json +++ b/utils/background_audios.json @@ -1,23 +1,13 @@ { "__comment": "Supported Backgrounds Audio. Can add/remove background audio here...", - "lofi": [ - "https://www.youtube.com/watch?v=LTphVIore3A", - "lofi.mp3", - "Super Lofi World" - ], - "lofi-2":[ - "https://www.youtube.com/watch?v=BEXL80LS0-I", - "lofi-2.mp3", - "stompsPlaylist" - ], "eerie":[ "https://www.youtube.com/watch?v=OozBNFEa4PM", "eerie.mp3", "Royalty Free Music" ], - "chill-summer":[ - "https://www.youtube.com/watch?v=EZE8JagnBI8", - "chill-summer.mp3", - "Mellow Vibes Radio" + "hybrid":[ + "https://www.youtube.com/watch?v=QbqkR5VNaU8", + "hybrid.mp3", + "Royalty Free Music" ] } diff --git a/utils/background_videos.json b/utils/background_videos.json index 6e00992..ad3899a 100644 --- a/utils/background_videos.json +++ b/utils/background_videos.json @@ -1,63 +1,15 @@ { "__comment": "Supported Backgrounds. Can add/remove background video here...", - "motor-gta": [ - "https://www.youtube.com/watch?v=vw5L4xCPy9Q", - "bike-parkour-gta.mp4", - "Achy Gaming", - "center" - ], - "rocket-league": [ - "https://www.youtube.com/watch?v=2X9QGY__0II", - "rocket_league.mp4", - "Orbital Gameplay", - "center" - ], - "minecraft": [ - "https://www.youtube.com/watch?v=n_Dv4JMiwK8", - "parkour.mp4", - "bbswitzer", - "center" - ], - "gta": [ - "https://www.youtube.com/watch?v=qGa9kWREOnE", - "gta-stunt-race.mp4", - "Achy Gaming", - "center" - ], - "csgo-surf": [ - "https://www.youtube.com/watch?v=E-8JlyO59Io", - "csgo-surf.mp4", - "Aki", - "center" - ], - "cluster-truck": [ - "https://www.youtube.com/watch?v=uVKxtdMgJVU", - "cluster_truck.mp4", - "No Copyright Gameplay", - "center" - ], - "minecraft-2": [ - "https://www.youtube.com/watch?v=Pt5_GSKIWQM", - "minecraft-2.mp4", - "Itslpsn", - "center" - ], - "multiversus": [ - "https://www.youtube.com/watch?v=66oK1Mktz6g", - "multiversus.mp4", - "MKIceAndFire", - "center" - ], - "fall-guys": [ - "https://www.youtube.com/watch?v=oGSsgACIc6Q", - "fall-guys.mp4", - "Throneful", + "mudrunner": [ + "https://www.youtube.com/watch?v=YsfkZoXv4s8", + "mudrunner.mp4", + "joel", "center" ], - "steep": [ - "https://www.youtube.com/watch?v=EnGiQrWBrko", - "steep.mp4", - "joel", + "granny-remake": [ + "https://www.youtube.com/watch?v=Ynjc1EcRExk", + "granny.mp4", + "Dope Gameplays", "center" ] -} +} \ No newline at end of file diff --git a/utils/imagenarator.py b/utils/imagenarator.py index 67d08ee..9b8eea5 100644 --- a/utils/imagenarator.py +++ b/utils/imagenarator.py @@ -57,6 +57,7 @@ def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) -> """ Render Images for video """ + # return title = process_text(reddit_obj["thread_title"], False) texts = process_post(reddit_obj["thread_post"]) id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) diff --git a/utils/process_post.py b/utils/process_post.py index ef03370..defef84 100644 --- a/utils/process_post.py +++ b/utils/process_post.py @@ -1,6 +1,6 @@ def process_post(reddit_thread_post): texts = reddit_thread_post - threshold = 60 + threshold = 80 for i in range(len(texts)): if len(texts[i]) > threshold: texts[i] = split_text(texts[i], threshold) @@ -10,11 +10,6 @@ def split_text(text, threshold): text = text.split(' ') new_text = '' texts = [] - # for i in range(threshold+1,1,-1): - # if (len(text) / i) - (len(text) // i) >= 0.7: - # threshold = i - # # print("Found:", threshold) - # break for i in text: if new_text == '': @@ -22,9 +17,17 @@ def split_text(text, threshold): continue new_text += ' ' + i - if len(new_text) >= threshold: - texts.append(new_text) - new_text = '' + if len(new_text) >= int(0.75 * threshold): + go = True + # Make sure that the text left is not so short + if i != text[-1]: + left = ' '.join(text[i+1:]) + if len(left) < int(0.25 * threshold): + go = False + + if go: + texts.append(new_text) + new_text = '' if new_text != '': texts.append(new_text) diff --git a/video_creation/background.py b/video_creation/background.py index 8c15b40..c79cadc 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -1,9 +1,12 @@ import json +import os import random import re +import math from pathlib import Path from random import randrange from typing import Any, Tuple, Dict +from pydub import AudioSegment from moviepy.editor import VideoFileClip, AudioFileClip from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip @@ -116,7 +119,18 @@ def download_background_audio(background_config: Tuple[str, str, str]): with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([uri]) + sound = AudioSegment.from_file(f"./assets/backgrounds/audio/{credit}-{filename}") + new_sound = sound[:] + + loops = math.ceil(1800 / sound.duration_seconds) + for _ in range(loops): + new_sound += sound + + os.remove(f"./assets/backgrounds/audio/{credit}-{filename}") + new_sound.export(f"./assets/backgrounds/audio/{credit}-{filename}", format="mp3") + print_substep("Background audio downloaded successfully! 🎉", style="bold green") + print(f"The audio duration was extended to be: {new_sound.duration_seconds} seconds") def chop_background(background_config: Dict[str, Tuple], video_length: int, reddit_object: dict): diff --git a/video_creation/final_video.py b/video_creation/final_video.py index 8d551e9..972aa70 100644 --- a/video_creation/final_video.py +++ b/video_creation/final_video.py @@ -159,8 +159,8 @@ def make_final_video( print_step("Creating the final video 🎥") - # background_clip = ffmpeg.input(prepare_background(reddit_id, W=W, H=H)) - background_clip = ffmpeg.input(f"assets/temp/{reddit_id}/background_noaudio.mp4") + background_clip = ffmpeg.input(prepare_background(reddit_id, W=W, H=H)) + # background_clip = ffmpeg.input(f"assets/temp/{reddit_id}/background_noaudio.mp4") # Gather all audio clips audio_clips = list() @@ -174,12 +174,12 @@ def make_final_video( audio_clips = [ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")] audio_clips.insert(1, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/postaudio.mp3")) elif settings.config["settings"]["storymodemethod"] == 1: - # audio_clips = [ - # ffmpeg.input(f"assets/temp/{reddit_id}/mp3/postaudio-{i}.mp3") - # for i in track(range(number_of_clips + 1), "Collecting the audio files...") - # ] - # audio_clips.insert(0, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")) - pass + audio_clips = [ + ffmpeg.input(f"assets/temp/{reddit_id}/mp3/postaudio-{i}.mp3") + for i in track(range(number_of_clips + 1), "Collecting the audio files...") + ] + audio_clips.insert(0, ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")) + # pass else: audio_clips = [ @@ -195,10 +195,11 @@ def make_final_video( 0, float(ffmpeg.probe(f"assets/temp/{reddit_id}/mp3/title.mp3")["format"]["duration"]), ) - # audio_concat = ffmpeg.concat(*audio_clips, a=1, v=0) - # ffmpeg.output( - # audio_concat, f"assets/temp/{reddit_id}/audio.mp3", **{"b:a": "192k"} - # ).overwrite_output().run(quiet=True) + # Comment those as well when testing + audio_concat = ffmpeg.concat(*audio_clips, a=1, v=0) + ffmpeg.output( + audio_concat, f"assets/temp/{reddit_id}/audio.mp3", **{"b:a": "192k"} + ).overwrite_output().run(quiet=True) console.log(f"[bold green] Video Will Be: {length} Seconds Long")