chore: release 3.4.0 (#2426)

Co-authored-by: Jason Cameron <git@jasoncameron.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: RequieMa <57754488+RequieMa@users.noreply.github.com>
Co-authored-by: prafful <praffulsharma1230@gmail.com>
Co-authored-by: M. Sanaullah <thebolly@gmail.com>
Co-authored-by: embee <emilien.bev.com@gmail.com>
Co-authored-by: Rodrigo <55567123+rodrigodasilv@users.noreply.github.com>
Co-authored-by: Emilien Bevierre <44171454+emilienbev@users.noreply.github.com>
Co-authored-by: tkhmielnitzky <tkhmielnitzky@gmail.com>
Co-authored-by: bnfone <89687390+bnfone@users.noreply.github.com>
Co-authored-by: Cyteon <129582290+Cyteon@users.noreply.github.com>
This commit is contained in:
21 files changed
+320 -165

No files matched your search

+21 -19
View File
@@ -6,7 +6,7 @@ from random import randrange
from typing import Any, Dict, Tuple
import yt_dlp
from moviepy.editor import AudioFileClip, VideoFileClip
from moviepy import AudioFileClip, VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from utils import settings
@@ -14,26 +14,26 @@ from utils.console import print_step, print_substep
def load_background_options():
background_options = {}
_background_options = {}
# Load background videos
with open("./utils/background_videos.json") as json_file:
background_options["video"] = json.load(json_file)
_background_options["video"] = json.load(json_file)
# Load background audios
with open("./utils/background_audios.json") as json_file:
background_options["audio"] = json.load(json_file)
_background_options["audio"] = json.load(json_file)
# Remove "__comment" from backgrounds
del background_options["video"]["__comment"]
del background_options["audio"]["__comment"]
del _background_options["video"]["__comment"]
del _background_options["audio"]["__comment"]
for name in list(background_options["video"].keys()):
pos = background_options["video"][name][3]
for name in list(_background_options["video"].keys()):
pos = _background_options["video"][name][3]
if pos != "center":
background_options["video"][name][3] = lambda t: ("center", pos + t)
_background_options["video"][name][3] = lambda t: ("center", pos + t)
return background_options
return _background_options
def get_start_and_end_times(video_length: int, length_of_clip: int) -> Tuple[int, int]:
@@ -124,10 +124,11 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd
"""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
Args:
reddit_object (Dict[str,str]) : Reddit object
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"])
thread_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 . . .")
@@ -138,8 +139,8 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd
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")
background_audio = background_audio.subclipped(start_time_audio, end_time_audio)
background_audio.write_audiofile(f"assets/temp/{thread_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]}"
@@ -149,17 +150,18 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd
)
# Extract video subclip
try:
with VideoFileClip(f"assets/backgrounds/video/{video_choice}") as video:
new = video.subclipped(start_time_video, end_time_video)
new.write_videofile(f"assets/temp/{thread_id}/background.mp4")
except (OSError, IOError): # ffmpeg issue see #348
print_substep("FFMPEG issue. Trying again...")
ffmpeg_extract_subclip(
f"assets/backgrounds/video/{video_choice}",
start_time_video,
end_time_video,
targetname=f"assets/temp/{id}/background.mp4",
outputfile=f"assets/temp/{thread_id}/background.mp4",
)
except (OSError, IOError): # ffmpeg issue see #348
print_substep("FFMPEG issue. Trying again...")
with VideoFileClip(f"assets/backgrounds/video/{video_choice}") as video:
new = video.subclip(start_time_video, end_time_video)
new.write_videofile(f"assets/temp/{id}/background.mp4")
print_substep("Background video chopped successfully!", style="bold green")
return background_config["video"][2]
+57 -46
View File
@@ -19,6 +19,7 @@ from utils import settings
from utils.cleanup import cleanup
from utils.console import print_step, print_substep
from utils.fonts import getheight
from utils.id import extract_id
from utils.thumbnail import create_thumbnail
from utils.videos import save_data
@@ -92,7 +93,7 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str:
output_path,
an=None,
**{
"c:v": "h264",
"c:v": "h264_nvenc",
"b:v": "20M",
"b:a": "192k",
"threads": multiprocessing.cpu_count(),
@@ -108,19 +109,61 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str:
return output_path
def get_text_height(draw, text, font, max_width):
lines = textwrap.wrap(text, width=max_width)
total_height = 0
for line in lines:
_, _, _, height = draw.textbbox((0, 0), line, font=font)
total_height += height
return total_height
def create_fancy_thumbnail(image, text, text_color, padding, wrap=35):
"""
It will take the 1px from the middle of the template and will be resized (stretched) vertically to accommodate the extra height needed for the title.
"""
print_step(f"Creating fancy thumbnail for: {text}")
font_title_size = 47
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size)
image_width, image_height = image.size
lines = textwrap.wrap(text, width=wrap)
y = (
(image_height / 2)
- (((getheight(font, text) + (len(lines) * padding) / len(lines)) * len(lines)) / 2)
+ 30
)
draw = ImageDraw.Draw(image)
# Calculate text height to determine new image height
draw = ImageDraw.Draw(image)
text_height = get_text_height(draw, text, font, wrap)
lines = textwrap.wrap(text, width=wrap)
# This is -50 to reduce the empty space at the bottom of the image,
# change it as per your requirement if needed otherwise leave it.
new_image_height = image_height + text_height + padding * (len(lines) - 1) - 50
# Separate the image into top, middle (1px), and bottom parts
top_part_height = image_height // 2
middle_part_height = 1 # 1px height middle section
bottom_part_height = image_height - top_part_height - middle_part_height
top_part = image.crop((0, 0, image_width, top_part_height))
middle_part = image.crop((0, top_part_height, image_width, top_part_height + middle_part_height))
bottom_part = image.crop((0, top_part_height + middle_part_height, image_width, image_height))
# Stretch the middle part
new_middle_height = new_image_height - top_part_height - bottom_part_height
middle_part = middle_part.resize((image_width, new_middle_height))
# Create new image with the calculated height
new_image = Image.new("RGBA", (image_width, new_image_height))
# Paste the top, stretched middle, and bottom parts into the new image
new_image.paste(top_part, (0, 0))
new_image.paste(middle_part, (0, top_part_height))
new_image.paste(bottom_part, (0, top_part_height + new_middle_height))
# Draw the title text on the new image
draw = ImageDraw.Draw(new_image)
y = top_part_height + padding
for line in lines:
draw.text((120, y), line, font=font, fill=text_color, align="left")
y += get_text_height(draw, line, font, wrap) + padding
# Draw the username "PlotPulse" at the specific position
username_font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 30)
draw.text(
(205, 825),
@@ -130,39 +173,7 @@ def create_fancy_thumbnail(image, text, text_color, padding, wrap=35):
align="left",
)
if len(lines) == 3:
lines = textwrap.wrap(text, width=wrap + 10)
font_title_size = 40
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size)
y = (
(image_height / 2)
- (((getheight(font, text) + (len(lines) * padding) / len(lines)) * len(lines)) / 2)
+ 35
)
elif len(lines) == 4:
lines = textwrap.wrap(text, width=wrap + 10)
font_title_size = 35
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size)
y = (
(image_height / 2)
- (((getheight(font, text) + (len(lines) * padding) / len(lines)) * len(lines)) / 2)
+ 40
)
elif len(lines) > 4:
lines = textwrap.wrap(text, width=wrap + 10)
font_title_size = 30
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), font_title_size)
y = (
(image_height / 2)
- (((getheight(font, text) + (len(lines) * padding) / len(lines)) * len(lines)) / 2)
+ 30
)
for line in lines:
draw.text((120, y), line, font=font, fill=text_color, align="left")
y += getheight(font, line) + padding
return image
return new_image
def merge_background_audio(audio: ffmpeg, reddit_id: str):
@@ -204,7 +215,7 @@ def make_final_video(
opacity = settings.config["settings"]["opacity"]
reddit_id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
reddit_id = extract_id(reddit_obj)
allowOnlyTTSFolder: bool = (
settings.config["settings"]["background"]["enable_extra_audio"]
@@ -343,8 +354,8 @@ def make_final_video(
)
current_time += audio_clips_durations[i]
title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"])
idx = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
title = extract_id(reddit_obj, "thread_title")
idx = extract_id(reddit_obj)
title_thumb = reddit_obj["thread_title"]
filename = f"{name_normalize(title)[:251]}"
@@ -427,7 +438,7 @@ def make_final_video(
path,
f="mp4",
**{
"c:v": "h264",
"c:v": "h264_nvenc",
"b:v": "20M",
"b:a": "192k",
"threads": multiprocessing.cpu_count(),
@@ -457,7 +468,7 @@ def make_final_video(
path,
f="mp4",
**{
"c:v": "h264",
"c:v": "h264_nvenc",
"b:v": "20M",
"b:a": "192k",
"threads": multiprocessing.cpu_count(),
+13 -15
View File
@@ -34,7 +34,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
# ! Make sure the reddit screenshots folder exists
Path(f"assets/temp/{reddit_id}/png").mkdir(parents=True, exist_ok=True)
# set the theme and disable non-essential cookies
# set the theme and turn off non-essential cookies
if settings.config["settings"]["theme"] == "dark":
cookie_file = open("./video_creation/data/cookie-dark-mode.json", encoding="utf-8")
bgcolor = (33, 33, 36, 255)
@@ -60,7 +60,6 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
transparent = False
if storymode and settings.config["settings"]["storymodemethod"] == 1:
# for idx,item in enumerate(reddit_object["thread_post"]):
print_substep("Generating images...")
return imagemaker(
theme=bgcolor,
@@ -82,11 +81,15 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
dsf = (W // 600) + 1
context = browser.new_context(
locale=lang or "en-us",
locale=lang or "en-CA,en;q=0.9",
color_scheme="dark",
viewport=ViewportSize(width=W, height=H),
device_scale_factor=dsf,
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
user_agent=f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{browser.version}.0.0.0 Safari/537.36",
extra_http_headers={
"Dnt": "1",
"Sec-Ch-Ua": '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"',
},
)
cookies = json.load(cookie_file)
cookie_file.close()
@@ -107,17 +110,12 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
login_error_div = page.locator(".AnimatedForm__errorMessage").first
if login_error_div.is_visible():
login_error_message = login_error_div.inner_text()
if login_error_message.strip() == "":
# The div element is empty, no error
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",
)
exit()
print_substep(
"Your reddit credentials are incorrect! Please modify them accordingly in the config.toml file.",
style="red",
)
exit()
else:
pass
+2
View File
@@ -6,6 +6,7 @@ from TTS.aws_polly import AWSPolly
from TTS.elevenlabs import elevenlabs
from TTS.engine_wrapper import TTSEngine
from TTS.GTTS import GTTS
from TTS.openai_tts import OpenAITTS
from TTS.pyttsx import pyttsx
from TTS.streamlabs_polly import StreamlabsPolly
from TTS.TikTok import TikTok
@@ -21,6 +22,7 @@ TTSProviders = {
"TikTok": TikTok,
"pyttsx": pyttsx,
"ElevenLabs": elevenlabs,
"OpenAI": OpenAITTS,
}