From e56d09212ee743cbb46dbb47012bde695a9a29ff Mon Sep 17 00:00:00 2001 From: Mohamed Moataz Date: Wed, 27 Mar 2024 12:21:03 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9A=A1=20Bot=20modifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Modified the bot to work with audios with small duration even for long videos. - Added video scheduling functionality. - Added some creepy background videos. - Better text synchronization in storymode. --- .gitignore | 1 + main.py | 40 ++++++++++++++------- requirements.txt | Bin 366 -> 5058 bytes utils/.config.template.toml | 7 ++-- utils/background_audios.json | 18 +++------- utils/background_videos.json | 66 +++++----------------------------- utils/imagenarator.py | 1 + utils/process_post.py | 21 ++++++----- video_creation/background.py | 14 ++++++++ video_creation/final_video.py | 25 ++++++------- 10 files changed, 86 insertions(+), 107 deletions(-) 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 c9abc854f3a3af9ce6376a691614988840dc0f6b..9605669d038e2fa7a4ac870b01c113dbfda21a3c 100644 GIT binary patch literal 5058 zcmb7|-ELb|5QXv_(P!B)F)kRH>kfE6a8q=f{8baT3hK1MA!CjOVz8 zO31RCde5F&vu4fgef;-dlhT&GUYqhz=4Gw_eW%y3oRv$x2j%PXRhgD$xzidJyShHm z+DpAJdh6bK(X9-QdpFhl=(ulKuBzutMX`5V%!Fa52*k(#u)IF8`%$Y{f+cLQGlp52 zlv%meesbB!%6MMLVpQM6!fLPgQWlHy*hM2-a1V~Y$aAkaH(jiuR)&=kY>j)S)mR6| zpym~5ZWU!G4B% z)so7Z(HEWVL1^ZB<0sZ$)wSyy$B6M|Uck%SOkWGdQm;S(R_~tc+j)7?d#%4FQfjJ@ zID?KCVsFZQS52}=^|H57uEd_}uV;Nu^>3>^-<0q5z7-F9@zs`Z%eh_)`Pu8+N7I=CRS9>*{e`#z$o14%t}N zEBS*3)P3osqjYmZfgzT&s7X-wDMT3 z*J7t%WowE~S!wrNtHuC-ePyLTdJ1M>GE{DLrbd6(DwE4Sz+Toy75$CAhj%a;N%U^+ zzY*4(POWrSKRS6l*7~rUTwYqeu6OWouT>^q zL>YAFAnd)VncvHcD`-WB^6!-uvxJ!w`Uw5H59u1>Gwql~j*l1K&U~rvUrCX~XTI12 z(cx^To{LG<2o0wjxnoTpS*y35HB;lg{^mppD}4HFz$denZui;F?4`aal?jzRMD_3$m^0`>{1XpURtB-w@&WeD$@eIq#+(?Q3B%8( z2`Y7#`0;u4P|>)HT$5Bj7TjFi7vzDK&0Z+WQ)Dd5sp17YM6u4oF6IPYoKIjtpU%Zh zvDf;0C-yn*ISuj5Ezswj_oCs0)^@U6$cv|pGwf%v>3EFTW@;-2nErTX5BC600N*F5 z`lO4?O5PG;CGH7kt*;#{Pz!g^>+0XSiWy-=advQmoO0&O!@_=AaZ4rK>14*+Dz_zi zp#S2=`yfV~Swy}(*3Eb4P3_jDv|>%eK>wVvU;IsFb5l_|s_)URfKSIpc|HkYcA@*< zHV3V=QtjZFY2IK_ajcG|YGx6A7xRdU^(%Rh#zK{Gb9Ssy8y0yVWj-+p`+bDZVELeU z%t89x>V*fomCw)#R^0HSQp0W_xeZ1+;IlOz4hQ*@d13~`; zKy6W9?&t2q>{6=giUkJlAJ2NPRcGsjGbeiWAn(DKbBn1<1@is@8$53?LFm2Ec4W2{ zpFAZdf4MjIw{B_~fOb~$J3H9LwY0@T7Z*vN03GgxFN z<*P4oIA%8r#gr)Ukb)MQX&3vQDnS!;e&Lu%|o<^)9<~14!eR@h@8}U~M4OR4E!{=G$%1<}Gtc)*@WBx*yQ8@U#+>1ahf|Wu zpUx+0XrG)hu)~u(ec}lfd0YQvgB`a8o@z#w7uMo8CfDd$yXobrtOA*>@bD}g%%N*V z^CWQnnKo94e-YqDWW;pYr_XHoOCCSpVdl_gWcZ=x{!o7n;;9B?@Rj;uM$;KfF~Dk0 Hx~%3ug)H5g literal 366 zcmXYs%W{Mu5JmU>FLv>8G%na?Jyn?>Kt-hng#hz#lW$K$vV(q{K6mulOQkr94tXnJ zr2pijsimQo*4yn%DS;9N?EYy@I7^qIgy8h~igw*{smK-?gs5Li_1OQv3+$(EGl=H< zVNy<&Mg)j<+GtZ^iyX4wol*=rqeD6BBRQRYr0EOMItXXZ=}oc%2L@N`oTp0zUIge)0;0VY&x*Z=?k 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")