Merge branch 'develop' into dev

pull/1671/head
Syed Aman Raza 2 years ago committed by GitHub
commit 0a1ba17d0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,23 +0,0 @@
# This workflow was added by CodeSee. Learn more at https://codesee.io/
# This is v2.0 of this workflow file
on:
push:
branches:
- master
pull_request_target:
types: [opened, synchronize, reopened]
name: CodeSee
permissions: read-all
jobs:
codesee:
runs-on: ubuntu-latest
continue-on-error: true
name: Analyze the repo with CodeSee
steps:
- uses: Codesee-io/codesee-action@v2
with:
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
codesee-url: https://app.codesee.io

@ -0,0 +1,46 @@
import random
from elevenlabs import generate, save
from utils import settings
voices = [
"Adam",
"Antoni",
"Arnold",
"Bella",
"Domi",
"Elli",
"Josh",
"Rachel",
"Sam",
]
class elevenlabs:
def __init__(self):
self.max_chars = 2500
self.voices = voices
def run(self, text, filepath, random_voice: bool = False):
if random_voice:
voice = self.randomvoice()
else:
voice = str(
settings.config["settings"]["tts"]["elevenlabs_voice_name"]
).capitalize()
if settings.config["settings"]["tts"]["elevenlabs_api_key"]:
api_key = settings.config["settings"]["tts"]["elevenlabs_api_key"]
else:
raise ValueError(
"You didn't set an Elevenlabs API key! Please set the config variable ELEVENLABS_API_KEY to a valid API key."
)
audio = generate(
api_key=api_key, text=text, voice=voice, model="eleven_multilingual_v1"
)
save(audio=audio, filename=filepath)
def randomvoice(self):
return random.choice(self.voices)

@ -3,11 +3,8 @@ import re
from pathlib import Path
from typing import Tuple
# import sox
# from mutagen import MutagenError
# from mutagen.mp3 import MP3, HeaderNotFoundError
import numpy as np
import translators as ts
import translators
from moviepy.audio.AudioClip import AudioClip
from moviepy.audio.fx.volumex import volumex
from moviepy.editor import AudioFileClip
@ -61,16 +58,16 @@ class TTSEngine:
regex_urls = r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*"
comment["comment_body"] = re.sub(regex_urls, " ", comment["comment_body"])
comment["comment_body"] = comment["comment_body"].replace("\n", ". ")
comment["comment_body"] = re.sub(r'\bAI\b', 'A.I', comment["comment_body"])
comment["comment_body"] = re.sub(r'\bAGI\b', 'A.G.I', comment["comment_body"])
comment["comment_body"] = re.sub(r"\bAI\b", "A.I", comment["comment_body"])
comment["comment_body"] = re.sub(
r"\bAGI\b", "A.G.I", comment["comment_body"]
)
if comment["comment_body"][-1] != ".":
comment["comment_body"] += "."
comment["comment_body"] = comment["comment_body"].replace(". . .", ".")
comment["comment_body"] = comment["comment_body"].replace(".. . ", ".")
comment["comment_body"] = comment["comment_body"].replace(". . ", ".")
comment["comment_body"] = re.sub(r'\."\.', '".', comment["comment_body"])
print(comment["comment_body"])
def run(self) -> Tuple[int, int]:
Path(self.path).mkdir(parents=True, exist_ok=True)
@ -154,7 +151,11 @@ class TTSEngine:
print("OSError")
def call_tts(self, filename: str, text: str):
self.tts_module.run(text, filepath=f"{self.path}/{filename}.mp3")
self.tts_module.run(
text,
filepath=f"{self.path}/{filename}.mp3",
random_voice=settings.config["settings"]["tts"]["random_voice"],
)
# try:
# self.length += MP3(f"{self.path}/{filename}.mp3").info.length
# except (MutagenError, HeaderNotFoundError):
@ -185,6 +186,6 @@ def process_text(text: str, clean: bool = True):
new_text = sanitize_text(text) if clean else text
if lang:
print_substep("Translating Text...")
translated_text = ts.google(text, to_language=lang)
translated_text = translators.google(text, to_language=lang)
new_text = sanitize_text(translated_text)
return new_text

@ -26,7 +26,7 @@ from video_creation.screenshot_downloader import get_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3
from utils.ffmpeg_install import ffmpeg_install
__VERSION__ = "3.1"
__VERSION__ = "3.2"
print(
"""
@ -40,7 +40,7 @@ print(
)
# Modified by JasonLovesDoggo
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://reddit-video-maker-bot.netlify.app/"
"### Thanks for using this tool! Feel free to contribute to this project on GitHub! If you have any questions, feel free to join my Discord server or submit a GitHub issue. You can find solutions to many common problems in the documentation: https://reddit-video-maker-bot.netlify.app/"
)
checkversion(__VERSION__)
@ -132,10 +132,11 @@ if __name__ == "__main__":
shutdown()
except Exception as err:
config["settings"]["tts"]["tiktok_sessionid"] = "REDACTED"
config["settings"]["tts"]["elevenlabs_api_key"] = "REDACTED"
print_step(
f"Sorry, something went wrong with this version! Try again, and feel free to report this issue at GitHub or the Discord community.\n"
f"Version: {__VERSION__} \n"
f"Error: {err} \n"
f'Config: {config["settings"]}'
)
raise err
raise err

@ -107,20 +107,10 @@ def get_subreddit_threads(POST_ID: str):
if submission is None:
return get_subreddit_threads(POST_ID) # submission already done. rerun
if settings.config["settings"]["storymode"]:
if not submission.selftext:
print_substep("You are trying to use story mode on post with no post text")
exit()
else:
# Check for the length of the post text
if len(submission.selftext) > (
settings.config["settings"]["storymode_max_length"] or 2000
):
print_substep(
f"Post is too long ({len(submission.selftext)}), try with a different post. ({settings.config['settings']['storymode_max_length']} character limit)"
)
exit()
elif not submission.num_comments:
elif (
not submission.num_comments
and settings.config["settings"]["storymode"] == "false"
):
print_substep("No comments found. Skipping.")
exit()

@ -1,22 +1,23 @@
boto3==1.24.24
botocore==1.27.24
gTTS==2.2.4
boto3==1.26.142
botocore==1.29.142
gTTS==2.3.2
moviepy==1.0.3
playwright==1.23.0
praw==7.6.1
playwright==1.34.0
praw==7.7.0
prawcore~=2.3.0
requests==2.28.1
requests==2.31.0
rich==13.3.5
toml==0.10.2
translators==5.3.1
translators==5.7.6
pyttsx3==2.90
Pillow~=9.4.0
Pillow==9.5.0
tomlkit==0.11.8
Flask==2.3.2
clean-text==0.6.0
unidecode==1.3.2
spacy==3.4.1
torch==1.12.1
transformers==4.25.1
unidecode==1.3.6
spacy==3.5.3
torch==2.0.1
transformers==4.29.2
ffmpeg-python==0.2.0
elevenlabs==0.2.16
yt-dlp==2023.3.4

@ -30,6 +30,7 @@ storymodemethod= { optional = true, default = 1, example = 1, explanation = "Sty
storymode_max_length = { optional = true, default = 1000, example = 1000, explanation = "Max length of the storymode video in characters. 200 characters are approximately 50 seconds.", type = "int", nmin = 1, oob_error = "It's very hard to make a video under a second." }
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" }
[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" }
@ -42,7 +43,10 @@ background_thumbnail_font_size = { optional = true, type = "int", default = 96,
background_thumbnail_font_color = { optional = true, default = "255,255,255", example = "255,255,255", explanation = "Font color in RGB format for the thumbnail text" }
[settings.tts]
voice_choice = { optional = false, default = "streamlabspolly", options = ["streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", ], example = "tiktok", explanation = "The voice platform used for TTS generation. This can be left blank and you will be prompted to choose at runtime." }
voice_choice = { optional = false, default = "tiktok", options = ["elevenlabs", "streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", ], example = "tiktok", explanation = "The voice platform used for TTS generation. " }
random_voice = { optional = false, default = true, example = true, options = [true, false,], explanation = "Randomizes the voice used for each comment" }
elevenlabs_voice_name = { optional = false, default = "Bella", example = "Bella", explanation = "The voice used for elevenlabs", options = ["Adam", "Antoni", "Arnold", "Bella", "Domi", "Elli", "Josh", "Rachel", "Sam", ] }
elevenlabs_api_key = { optional = true, example = "21f13f91f54d741e2ae27d2ab1b99d59", explanation = "Elevenlabs API key" }
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" }

@ -22,24 +22,33 @@ def ffmpeg_install_windows():
for file in os.listdir("ffmpeg/doc"):
os.remove(f"ffmpeg/doc/{file}")
os.rmdir("ffmpeg/doc")
os.rmdir("ffmpeg/LICENSE.txt")
os.rmdir("ffmpeg/")
print("FFmpeg installed successfully! Please restart your computer and then re-run the program.")
exit()
except Exception as e:
print(
"An error occurred while trying to install FFmpeg. Please try again. Otherwise, please install FFmpeg manually and try again.")
"An error occurred while trying to install FFmpeg. Please try again. Otherwise, please install FFmpeg manually and try again."
)
print(e)
exit()
def ffmpeg_install_linux():
try:
subprocess.run("sudo apt install ffmpeg", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(
"sudo apt install ffmpeg",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except Exception as e:
print(
"An error occurred while trying to install FFmpeg. Please try again. Otherwise, please install FFmpeg manually and try again.")
"An error occurred while trying to install FFmpeg. Please try again. Otherwise, please install FFmpeg manually and try again."
)
print(e)
exit()
print("FFmpeg installed successfully! Please re-run the program.")
@ -48,10 +57,16 @@ def ffmpeg_install_linux():
def ffmpeg_install_mac():
try:
subprocess.run("brew install ffmpeg", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run(
"brew install ffmpeg",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except FileNotFoundError:
print(
"Homebrew is not installed. Please install it and try again. Otherwise, please install FFmpeg manually and try again.")
"Homebrew is not installed. Please install it and try again. Otherwise, please install FFmpeg manually and try again."
)
exit()
print("FFmpeg installed successfully! Please re-run the program.")
exit()
@ -65,7 +80,7 @@ def ffmpeg_install():
if not os.path.exists("./results") :
print('FFmpeg is installed on this system! If you are seeing this error for the second time, restart your computer.')
print('FFmpeg is not installed on this system.')
resp = input("We can try to automatically install it for you. Would you like to do that? (y/n): ")
if resp.lower() == "y":
print("Installing FFmpeg...")
if os.name == "nt":
@ -75,12 +90,16 @@ def ffmpeg_install():
elif os.name == "mac":
ffmpeg_install_mac()
else:
print("Your OS is not supported. Please install FFmpeg manually and try again.")
print(
"Your OS is not supported. Please install FFmpeg manually and try again."
)
exit()
else:
print("Please install FFmpeg manually and try again.")
exit()
except Exception as e:
print("Welcome fellow traveler! You're one of the few who have made it this far. We have no idea how you got at this error, but we're glad you're here. Please report this error to the developer, and we'll try to fix it as soon as possible. Thank you for your patience!")
print(
"Welcome fellow traveler! You're one of the few who have made it this far. We have no idea how you got at this error, but we're glad you're here. Please report this error to the developer, and we'll try to fix it as soon as possible. Thank you for your patience!"
)
print(e)
return None

@ -57,20 +57,18 @@ def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) ->
"""
Render Images for video
"""
title = process_text(
reddit_obj["thread_title"], False
)
title = process_text(reddit_obj["thread_title"], False)
texts = reddit_obj["thread_post"]
id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
if transparent:
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 50)
tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 50)
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100)
tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100)
else:
tfont = ImageFont.truetype(
os.path.join("fonts", "Roboto-Bold.ttf"), 100
) # for title
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 90)
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 100)
size = (1920, 1080)
image = Image.new("RGBA", size, theme)

@ -0,0 +1,7 @@
def clear_cookie_by_name(context, cookie_cleared_name):
cookies = context.cookies()
filtered_cookies = [
cookie for cookie in cookies if cookie["name"] != cookie_cleared_name
]
context.clear_cookies()
context.add_cookies(filtered_cookies)

@ -57,6 +57,23 @@ def get_subreddit_undone(
f'This post has under the specified minimum of comments ({settings.config["reddit"]["thread"]["min_comments"]}). Skipping...'
)
continue
if settings.config["settings"]["storymode"]:
if not submission.selftext:
print_substep(
"You are trying to use story mode on post with no post text"
)
continue
else:
# Check for the length of the post text
if len(submission.selftext) > (
settings.config["settings"]["storymode_max_length"] or 2000
):
print_substep(
f"Post is too long ({len(submission.selftext)}), try with a different post. ({settings.config['settings']['storymode_max_length']} character limit)"
)
continue
elif len(submission.selftext) < 30:
continue
if settings.config["settings"]["storymode"] and not submission.is_self:
continue
if similarity_scores is not None:
@ -73,7 +90,7 @@ def get_subreddit_undone(
] # set doesn't have __getitem__
index = times_checked + 1
if index == len(VALID_TIME_FILTERS):
print("all time filters have been checked you absolute madlad ")
print("All submissions have been done.")
return get_subreddit_undone(
subreddit.top(

@ -3,14 +3,15 @@ import random
import re
from pathlib import Path
from random import randrange
from typing import Any, Tuple,Dict
from typing import Any, Tuple, Dict
from moviepy.editor import VideoFileClip,AudioFileClip
from moviepy.editor import VideoFileClip, AudioFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from utils import settings
from utils.console import print_step, print_substep
import yt_dlp
def load_background_options():
background_options = {}
# Load background videos
@ -20,11 +21,11 @@ def load_background_options():
# Load background audios
with open("./utils/background_audios.json") as json_file:
background_options["audio"] = json.load(json_file)
# Remove "__comment" from backgrounds
del background_options["video"]["__comment"]
del background_options["audio"]["__comment"]
for name in list(background_options["video"].keys()):
pos = background_options["video"][name][3]
@ -34,7 +35,6 @@ def load_background_options():
return background_options
def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]:
"""Generates a random interval of time to be used as the background of the video.
@ -47,11 +47,11 @@ def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int
"""
initialValue = 180
# Issue #1649 - Ensures that will be a valid interval in the video
while(int(length_of_clip) <= int(video_length+initialValue)):
if(initialValue == initialValue //2):
while int(length_of_clip) <= int(video_length + initialValue):
if initialValue == initialValue // 2:
raise Exception("Your background is too short for this video length")
else:
initialValue //= 2 #Divides the initial value by 2 until reach 0
initialValue //= 2 # Divides the initial value by 2 until reach 0
random_time = randrange(initialValue, int(length_of_clip) - int(video_length))
return random_time, random_time + video_length
@ -73,6 +73,7 @@ def get_background_config(mode: str):
return background_options[mode][choice]
def download_background_video(background_config: Tuple[str, str, str, Any]):
"""Downloads the background/s video from YouTube."""
Path("./assets/backgrounds/video/").mkdir(parents=True, exist_ok=True)
@ -86,7 +87,7 @@ 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}")
ydl_opts = {
'format': "bestvideo[height<=1080][ext=mp4]",
"format": "bestvideo[height<=1080][ext=mp4]",
"outtmpl": f"assets/backgrounds/video/{credit}-{filename}",
"retries": 10,
}
@ -95,6 +96,7 @@ def download_background_video(background_config: Tuple[str, str, str, Any]):
ydl.download(uri)
print_substep("Background video downloaded successfully! 🎉", style="bold green")
def download_background_audio(background_config: Tuple[str, str, str]):
"""Downloads the background/s audio from YouTube."""
Path("./assets/backgrounds/audio/").mkdir(parents=True, exist_ok=True)
@ -108,9 +110,9 @@ def download_background_audio(background_config: Tuple[str, str, str]):
print_substep("Downloading the backgrounds audio... please be patient 🙏 ")
print_substep(f"Downloading {filename} from {uri}")
ydl_opts = {
'outtmpl': f'./assets/backgrounds/audio/{credit}-{filename}',
'format': 'bestaudio/best',
'extract_audio': True,
"outtmpl": f"./assets/backgrounds/audio/{credit}-{filename}",
"format": "bestaudio/best",
"extract_audio": True,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
@ -119,9 +121,8 @@ def download_background_audio(background_config: Tuple[str, str, str]):
print_substep("Background audio downloaded successfully! 🎉", style="bold green")
def chop_background(
background_config: Dict[str,Tuple], video_length: int, reddit_object: dict
background_config: Dict[str, Tuple], video_length: int, reddit_object: dict
):
"""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
@ -131,20 +132,26 @@ def chop_background(
"""
id = re.sub(r"[^\w\s-]", "", reddit_object["thread_id"])
if(settings.config["settings"]["background"][f"background_audio_volume"] == 0):
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]}"
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)
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]}"
background_video = VideoFileClip(f"assets/backgrounds/video/{video_choice}")
start_time_video, end_time_video = get_start_and_end_times(video_length, background_video.duration)
start_time_video, end_time_video = get_start_and_end_times(
video_length, background_video.duration
)
# Extract video subclip
try:
ffmpeg_extract_subclip(
@ -161,5 +168,6 @@ def chop_background(
print_substep("Background video chopped successfully!", style="bold green")
return background_config["video"][2]
# Create a tuple for downloads background (background_audio_options, background_video_options)
background_options = load_background_options()

@ -1,13 +1,12 @@
import multiprocessing
import os
import re
import shutil
from os.path import exists # Needs to be imported specifically
from os.path import exists # Needs to be imported specifically
from typing import Final
from typing import Tuple, Any, Dict
import ffmpeg
import translators as ts
import translators
from PIL import Image
from rich.console import Console
from rich.progress import track
@ -18,12 +17,12 @@ from utils.thumbnail import create_thumbnail
from utils.videos import save_data
from utils import settings
console = Console()
import tempfile
import threading
import time
console = Console()
class ProgressFfmpeg(threading.Thread):
def __init__(self, vid_duration_seconds, progress_update_callback):
@ -73,7 +72,7 @@ def name_normalize(name: str) -> str:
lang = settings.config["reddit"]["thread"]["post_lang"]
if lang:
print_substep("Translating filename...")
translated_name = ts.google(name, to_language=lang)
translated_name = translators.google(name, to_language=lang)
return translated_name
else:
return name
@ -103,34 +102,34 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str:
exit(1)
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
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,
)
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
return merged_audio # Return merged audio
def make_final_video(
number_of_clips: int,
length: int,
reddit_obj: dict,
background_config: Dict[str,Tuple],
background_config: Dict[str, Tuple],
):
"""Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp
Args:
@ -143,10 +142,14 @@ def make_final_video(
W: Final[int] = int(settings.config["settings"]["resolution_w"])
H: Final[int] = int(settings.config["settings"]["resolution_h"])
opacity = settings.config["settings"]["opacity"]
reddit_id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
allowOnlyTTSFolder: bool = settings.config["settings"]["background"]["enable_extra_audio"] \
and settings.config["settings"]["background"]["background_audio_volume"] != 0
allowOnlyTTSFolder: bool = (
settings.config["settings"]["background"]["enable_extra_audio"]
and settings.config["settings"]["background"]["background_audio_volume"] != 0
)
print_step("Creating the final video 🎥")
@ -154,6 +157,11 @@ def make_final_video(
# Gather all audio clips
audio_clips = list()
if number_of_clips == 0 and settings.config["settings"]["storymode"] == "false":
print(
"No audio clips to gather. Please use a different TTS or post."
) # This is to fix the TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
exit()
if settings.config["settings"]["storymode"]:
if settings.config["settings"]["storymodemethod"] == 0:
audio_clips = [ffmpeg.input(f"assets/temp/{reddit_id}/mp3/title.mp3")]
@ -203,7 +211,7 @@ def make_final_video(
screenshot_width = int((W * 45) // 100)
audio = ffmpeg.input(f"assets/temp/{reddit_id}/audio.mp3")
final_audio = merge_background_audio(audio,reddit_id)
final_audio = merge_background_audio(audio, reddit_id)
image_clips = list()
@ -269,8 +277,9 @@ def make_final_video(
"v"
].filter("scale", screenshot_width, -1)
)
image_overlay = image_clips[i].filter("colorchannelmixer", aa=opacity)
background_clip = background_clip.overlay(
image_clips[i],
image_overlay,
enable=f"between(t,{current_time},{current_time + audio_clips_durations[i]})",
x="(main_w-overlay_w)/2",
y="(main_h-overlay_h)/2",
@ -285,11 +294,15 @@ def make_final_video(
subreddit = settings.config["reddit"]["thread"]["subreddit"]
if not exists(f"./results/{subreddit}"):
print_substep("The 'results' folder could not be found so it was automatically created.")
print_substep(
"The 'results' folder could not be found so it was automatically created."
)
os.makedirs(f"./results/{subreddit}")
if not exists(f"./results/{subreddit}/OnlyTTS") and allowOnlyTTSFolder:
print_substep("The 'OnlyTTS' folder could not be found so it was automatically created.")
print_substep(
"The 'OnlyTTS' folder could not be found so it was automatically created."
)
os.makedirs(f"./results/{subreddit}/OnlyTTS")
# create a thumbnail for the video
@ -297,7 +310,9 @@ def make_final_video(
if settingsbackground["background_thumbnail"]:
if not exists(f"./results/{subreddit}/thumbnails"):
print_substep("The 'results/thumbnails' folder could not be found so it was automatically created.")
print_substep(
"The 'results/thumbnails' folder could not be found so it was automatically created."
)
os.makedirs(f"./results/{subreddit}/thumbnails")
# get the first file with the .png extension from assets/backgrounds and use it as a background for the thumbnail
first_image = next(
@ -341,6 +356,7 @@ def make_final_video(
fontcolor="White",
fontfile=os.path.join("fonts", "Roboto-Regular.ttf"),
)
background_clip = background_clip.filter("scale", W, H)
print_step("Rendering the video 🎥")
from tqdm import tqdm
@ -354,11 +370,13 @@ def make_final_video(
defaultPath = f"results/{subreddit}"
with ProgressFfmpeg(length, on_update_example) as progress:
path = defaultPath + f"/{filename}"
path = path[:251] + ".mp4" #Prevent a error by limiting the path length, do not change this.
path = (
path[:251] + ".mp4"
) # Prevent a error by limiting the path length, do not change this.
ffmpeg.output(
background_clip,
final_audio,
path,
path,
f="mp4",
**{
"c:v": "h264",
@ -374,9 +392,11 @@ def make_final_video(
)
old_percentage = pbar.n
pbar.update(100 - old_percentage)
if(allowOnlyTTSFolder):
if allowOnlyTTSFolder:
path = defaultPath + f"/OnlyTTS/{filename}"
path = path[:251] + ".mp4" #Prevent a error by limiting the path length, do not change this.
path = (
path[:251] + ".mp4"
) # Prevent a error by limiting the path length, do not change this.
print_step("Rendering the Only TTS Video 🎥")
with ProgressFfmpeg(length, on_update_example) as progress:
try:
@ -400,12 +420,12 @@ def make_final_video(
except ffmpeg.Error as e:
print(e.stderr.decode("utf8"))
exit(1)
old_percentage = pbar.n
pbar.update(100 - old_percentage)
pbar.close()
save_data(subreddit, filename + ".mp4", title, idx, background_config['video'][2])
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 📁")

@ -3,7 +3,7 @@ import re
from pathlib import Path
from typing import Dict, Final
import translators as ts
import translators
from playwright.async_api import async_playwright # pylint: disable=unused-import
from playwright.sync_api import ViewportSize, sync_playwright
from rich.progress import track
@ -11,6 +11,7 @@ from rich.progress import track
from utils import settings
from utils.console import print_step, print_substep
from utils.imagenarator import imagemaker
from utils.playwright import clear_cookie_by_name
from utils.videos import save_data
@ -115,7 +116,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
)
page.locator("button[class$='m-full-width']").click()
page.wait_for_timeout(5000)
login_error_div = page.locator(".AnimatedForm__errorMessage").first
if login_error_div.is_visible():
login_error_message = login_error_div.inner_text()
@ -124,12 +125,22 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
pass
else:
# The div contains an error message
print_substep("Your reddit credentials are incorrect! Please modify them accordingly in the config.toml file.", style="red")
print_substep(
"Your reddit credentials are incorrect! Please modify them accordingly in the config.toml file.",
style="red",
)
exit()
else:
pass
page.wait_for_load_state()
# Handle the redesign
# Check if the redesign optout cookie is set
if page.locator("#redesign-beta-optin-btn").is_visible():
# Clear the redesign optout cookie
clear_cookie_by_name(context, "redesign_optout")
# Reload the page for the redesign to take effect
page.reload()
# Get the thread screenshot
page.goto(reddit_object["thread_url"], timeout=0)
page.set_viewport_size(ViewportSize(width=W, height=H))
@ -157,13 +168,13 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
if lang:
print_substep("Translating post...")
texts_in_tl = ts.google(
texts_in_tl = translators.google(
reddit_object["thread_title"],
to_language=lang,
)
page.evaluate(
"tl_content => document.querySelector('[data-test-id=\"post-content\"] > div:nth-child(3) > div > div').textContent = tl_content",
"tl_content => document.querySelector('[data-adclicklocation=\"title\"] > div > div > h1').textContent = tl_content",
texts_in_tl,
)
else:
@ -171,9 +182,20 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
postcontentpath = f"assets/temp/{reddit_id}/png/title.png"
try:
page.locator('[data-test-id="post-content"]').screenshot(
path=postcontentpath
)
if settings.config["settings"]["zoom"] != 1:
# store zoom settings
zoom = settings.config["settings"]["zoom"]
# zoom the body of the page
page.evaluate("document.body.style.zoom=" + str(zoom))
# as zooming the body doesn't change the properties of the divs, we need to adjust for the zoom
location = page.locator('[data-test-id="post-content"]').bounding_box()
for i in location:
location[i] = float("{:.2f}".format(location[i] * zoom))
page.screenshot(clip=location, path=postcontentpath)
else:
page.locator('[data-test-id="post-content"]').screenshot(
path=postcontentpath
)
except Exception as e:
print_substep("Something went wrong!", style="red")
resp = input(
@ -218,7 +240,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
# translate code
if settings.config["reddit"]["thread"]["post_lang"]:
comment_tl = ts.google(
comment_tl = translators.google(
comment["comment_body"],
to_language=settings.config["reddit"]["thread"]["post_lang"],
)
@ -227,9 +249,29 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
[comment_tl, comment["comment_id"]],
)
try:
page.locator(f"#t1_{comment['comment_id']}").screenshot(
path=f"assets/temp/{reddit_id}/png/comment_{idx}.png"
)
if settings.config["settings"]["zoom"] != 1:
# store zoom settings
zoom = settings.config["settings"]["zoom"]
# zoom the body of the page
page.evaluate("document.body.style.zoom=" + str(zoom))
# scroll comment into view
page.locator(
f"#t1_{comment['comment_id']}"
).scroll_into_view_if_needed()
# as zooming the body doesn't change the properties of the divs, we need to adjust for the zoom
location = page.locator(
f"#t1_{comment['comment_id']}"
).bounding_box()
for i in location:
location[i] = float("{:.2f}".format(location[i] * zoom))
page.screenshot(
clip=location,
path=f"assets/temp/{reddit_id}/png/comment_{idx}.png",
)
else:
page.locator(f"#t1_{comment['comment_id']}").screenshot(
path=f"assets/temp/{reddit_id}/png/comment_{idx}.png"
)
except TimeoutError:
del reddit_object["comments"]
screenshot_num += 1

@ -7,6 +7,7 @@ from TTS.TikTok import TikTok
from TTS.aws_polly import AWSPolly
from TTS.engine_wrapper import TTSEngine
from TTS.pyttsx import pyttsx
from TTS.elevenlabs import elevenlabs
from TTS.streamlabs_polly import StreamlabsPolly
from utils import settings
from utils.console import print_table, print_step
@ -19,6 +20,7 @@ TTSProviders = {
"StreamlabsPolly": StreamlabsPolly,
"TikTok": TikTok,
"pyttsx": pyttsx,
"ElevenLabs": elevenlabs,
}

Loading…
Cancel
Save