Merge pull request #2060 from elebumm/develop

Update 3.3.0
pull/2094/head 3.3.0
Jason Cameron 1 year ago committed by GitHub
commit c68c5808cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -39,7 +39,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

@ -3,30 +3,35 @@
# Othewrwise, Black is run and its changes are committed back to the incoming pull request. # Othewrwise, Black is run and its changes are committed back to the incoming pull request.
# https://github.com/cclauss/autoblack # https://github.com/cclauss/autoblack
name: autoblack name: fmt
on: on:
push: push:
branches: ["develop"] branches: ["develop"]
jobs: jobs:
build: format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v4
- name: Set up Python 3.7 - name: Set up Python 3.10
uses: actions/setup-python@v1 uses: actions/setup-python@v5
with: with:
python-version: 3.9 python-version: 3.10.14
- name: Install Black - name: Install Black & isort
run: pip install black run: pip install black isort
- name: Run black --check . - name: Run black check
run: black --check . run: black --check . --line-length 101
- name: If needed, commit black changes to the pull request - name: Run isort check
run: isort . --check-only --diff --profile black
- name: If needed, commit changes to the pull request
if: failure() if: failure()
run: | run: |
black . --line-length 101 black . --line-length 101
isort . --profile black
git config --global user.name github-actions git config --global user.name github-actions
git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
git checkout $GITHUB_HEAD_REF git checkout $GITHUB_HEAD_REF
git commit -am "fixup: Format Python code with Black" git commit -am "fixup: Format Python code with Black"
git push origin HEAD:develop git push origin HEAD:develop

@ -6,7 +6,10 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: psf/black@stable - uses: psf/black@stable
with: with:
options: "--line-length 101" options: "--line-length 101"
- uses: isort/isort-action@v1
with:
configuration: "--check-only --diff --profile black"

5
.gitignore vendored

@ -231,7 +231,8 @@ fabric.properties
# Android studio 3.1+ serialized cache file # Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser .idea/caches/build_file_checksums.ser
assets/ assets/temp
assets/backgrounds
/.vscode /.vscode
out out
.DS_Store .DS_Store
@ -244,4 +245,4 @@ video_creation/data/videos.json
video_creation/data/envvars.txt video_creation/data/envvars.txt
config.toml config.toml
*.exe *.exe

@ -1,4 +1,4 @@
FROM python:3.10.9-slim FROM python:3.10.14-slim
RUN apt update RUN apt update
RUN apt-get install -y ffmpeg RUN apt-get install -y ffmpeg
@ -9,8 +9,4 @@ ADD . /app
WORKDIR /app WORKDIR /app
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
# tricks for pytube : https://github.com/elebumm/RedditVideoMakerBot/issues/142
# (NOTE : This is no longer useful since pytube was removed from the dependencies)
# RUN sed -i 's/re.compile(r"^\\w+\\W")/re.compile(r"^\\$*\\w+\\W")/' /usr/local/lib/python3.8/dist-packages/pytube/cipher.py
CMD ["python3", "main.py"] CMD ["python3", "main.py"]

@ -205,7 +205,7 @@
<select name="background_choice" class="form-select" data-toggle="tooltip" <select name="background_choice" class="form-select" data-toggle="tooltip"
data-original-title='Sets the background of the video'> data-original-title='Sets the background of the video'>
<option value=" ">Random Video</option> <option value=" ">Random Video</option>
{% for background in checks["background_choice"]["options"][1:] %} {% for background in checks["background_video"]["options"][1:] %}
<option value="{{background}}">{{background}}</option> <option value="{{background}}">{{background}}</option>
{% endfor %} {% endfor %}
</select> </select>
@ -618,4 +618,4 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

@ -50,7 +50,7 @@ On macOS and Linux (debian, arch, fedora and centos, and based on those), you ca
This can also be used to update the installation This can also be used to update the installation
4. Run `python main.py` 4. Run `python main.py`
5. Visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps), and set up an app that is a "script". Paste any URL in redirect URL. Ex:google.com 5. Visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps), and set up an app that is a "script". Paste any URL in redirect URL. Ex:`https://jasoncameron.dev`
6. The bot will ask you to fill in your details to connect to the Reddit API, and configure the bot to your liking 6. The bot will ask you to fill in your details to connect to the Reddit API, and configure the bot to your liking
7. Enjoy 😎 7. Enjoy 😎
8. If you need to reconfigure the bot, simply open the `config.toml` file and delete the lines that need to be changed. On the next run of the bot, it will help you reconfigure those options. 8. If you need to reconfigure the bot, simply open the `config.toml` file and delete the lines that need to be changed. On the next run of the bot, it will help you reconfigure those options.
@ -81,7 +81,7 @@ I have tried to simplify the code so anyone can read it and start contributing a
Please read our [contributing guidelines](CONTRIBUTING.md) for more detailed information. Please read our [contributing guidelines](CONTRIBUTING.md) for more detailed information.
### For any questions or support join the [Discord](https://discord.gg/WBQT52RrHV) server ### For any questions or support join the [Discord](https://discord.gg/qfQSx45xCV) server
## Developers and maintainers. ## Developers and maintainers.
@ -101,6 +101,8 @@ Freebiell (Freebie#3263) - https://github.com/FreebieII
Aman Raza (electro199#8130) - https://github.com/electro199 Aman Raza (electro199#8130) - https://github.com/electro199
Cyteon (cyteon) - https://github.com/cyteon
## LICENSE ## LICENSE
[Roboto Fonts](https://fonts.google.com/specimen/Roboto/about) are licensed under [Apache License V2](https://www.apache.org/licenses/LICENSE-2.0) [Roboto Fonts](https://fonts.google.com/specimen/Roboto/about) are licensed under [Apache License V2](https://www.apache.org/licenses/LICENSE-2.0)

@ -2,7 +2,7 @@
import base64 import base64
import random import random
import time import time
from typing import Optional, Final from typing import Final, Optional
import requests import requests

@ -1,33 +1,28 @@
import random import random
from elevenlabs import generate, save from elevenlabs import save
from elevenlabs.client import ElevenLabs
from utils import settings from utils import settings
voices = [
"Adam",
"Antoni",
"Arnold",
"Bella",
"Domi",
"Elli",
"Josh",
"Rachel",
"Sam",
]
class elevenlabs: class elevenlabs:
def __init__(self): def __init__(self):
self.max_chars = 2500 self.max_chars = 2500
self.voices = voices self.client: ElevenLabs = None
def run(self, text, filepath, random_voice: bool = False): def run(self, text, filepath, random_voice: bool = False):
if self.client is None:
self.initialize()
if random_voice: if random_voice:
voice = self.randomvoice() voice = self.randomvoice()
else: else:
voice = str(settings.config["settings"]["tts"]["elevenlabs_voice_name"]).capitalize() voice = str(settings.config["settings"]["tts"]["elevenlabs_voice_name"]).capitalize()
audio = self.client.generate(text=text, voice=voice, model="eleven_multilingual_v1")
save(audio=audio, filename=filepath)
def initialize(self):
if settings.config["settings"]["tts"]["elevenlabs_api_key"]: if settings.config["settings"]["tts"]["elevenlabs_api_key"]:
api_key = settings.config["settings"]["tts"]["elevenlabs_api_key"] api_key = settings.config["settings"]["tts"]["elevenlabs_api_key"]
else: else:
@ -35,8 +30,9 @@ class elevenlabs:
"You didn't set an Elevenlabs API key! Please set the config variable ELEVENLABS_API_KEY to a valid API key." "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") self.client = ElevenLabs(api_key=api_key)
save(audio=audio, filename=filepath)
def randomvoice(self): def randomvoice(self):
return random.choice(self.voices) if self.client is None:
self.initialize()
return random.choice(self.client.voices.get_all().voices).voice_name

@ -14,14 +14,12 @@ from utils import settings
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
from utils.voice import sanitize_text from utils.voice import sanitize_text
DEFAULT_MAX_LENGTH: int = ( DEFAULT_MAX_LENGTH: int = (
50 # Video length variable, edit this on your own risk. It should work, but it's not supported 50 # Video length variable, edit this on your own risk. It should work, but it's not supported
) )
class TTSEngine: class TTSEngine:
"""Calls the given TTS engine to reduce code duplication and allow multiple TTS engines. """Calls the given TTS engine to reduce code duplication and allow multiple TTS engines.
Args: Args:

@ -43,9 +43,11 @@ class StreamlabsPolly:
f"Please set the config variable STREAMLABS_POLLY_VOICE to a valid voice. options are: {voices}" f"Please set the config variable STREAMLABS_POLLY_VOICE to a valid voice. options are: {voices}"
) )
voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize() voice = str(settings.config["settings"]["tts"]["streamlabs_polly_voice"]).capitalize()
body = {"voice": voice, "text": text, "service": "polly"} body = {"voice": voice, "text": text, "service": "polly"}
headers = {"Referer": "https://streamlabs.com/"} headers = {"Referer": "https://streamlabs.com/"}
response = requests.post(self.url, headers=headers, data=body) response = requests.post(self.url, headers=headers, data=body)
if not check_ratelimit(response): if not check_ratelimit(response):
self.run(text, filepath, random_voice) self.run(text, filepath, random_voice)

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@ -7,25 +7,25 @@ from subprocess import Popen
from typing import NoReturn from typing import NoReturn
from prawcore import ResponseException from prawcore import ResponseException
from utils.console import print_substep
from reddit.subreddit import get_subreddit_threads from reddit.subreddit import get_subreddit_threads
from utils import settings from utils import settings
from utils.cleanup import cleanup from utils.cleanup import cleanup
from utils.console import print_markdown, print_step from utils.console import print_markdown, print_step, print_substep
from utils.ffmpeg_install import ffmpeg_install
from utils.id import id from utils.id import id
from utils.version import checkversion from utils.version import checkversion
from video_creation.background import ( from video_creation.background import (
download_background_video,
download_background_audio,
chop_background, chop_background,
download_background_audio,
download_background_video,
get_background_config, get_background_config,
) )
from video_creation.final_video import make_final_video from video_creation.final_video import make_final_video
from video_creation.screenshot_downloader import get_screenshots_of_reddit_posts from video_creation.screenshot_downloader import get_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3 from video_creation.voices import save_text_to_mp3
from utils.ffmpeg_install import ffmpeg_install
__VERSION__ = "3.2.1" __VERSION__ = "3.3.0"
print( print(
""" """
@ -37,7 +37,6 @@ print(
""" """
) )
# Modified by JasonLovesDoggo
print_markdown( print_markdown(
"### 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/" "### 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/"
) )

@ -1,18 +1,16 @@
import re import re
from prawcore.exceptions import ResponseException
from utils import settings
import praw import praw
from praw.models import MoreComments from praw.models import MoreComments
from prawcore.exceptions import ResponseException from prawcore.exceptions import ResponseException
from utils import settings
from utils.ai_methods import sort_by_similarity
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
from utils.posttextparser import posttextparser
from utils.subreddit import get_subreddit_undone from utils.subreddit import get_subreddit_undone
from utils.videos import check_done from utils.videos import check_done
from utils.voice import sanitize_text from utils.voice import sanitize_text
from utils.posttextparser import posttextparser
from utils.ai_methods import sort_by_similarity
def get_subreddit_threads(POST_ID: str): def get_subreddit_threads(POST_ID: str):
@ -106,7 +104,7 @@ def get_subreddit_threads(POST_ID: str):
upvotes = submission.score upvotes = submission.score
ratio = submission.upvote_ratio * 100 ratio = submission.upvote_ratio * 100
num_comments = submission.num_comments num_comments = submission.num_comments
threadurl = f"https://reddit.com{submission.permalink}" threadurl = f"https://new.reddit.com/{submission.permalink}"
print_substep(f"Video will be: {submission.title} :thumbsup:", style="bold green") print_substep(f"Video will be: {submission.title} :thumbsup:", style="bold green")
print_substep(f"Thread url is: {threadurl} :thumbsup:", style="bold green") print_substep(f"Thread url is: {threadurl} :thumbsup:", style="bold green")

@ -1,23 +1,24 @@
boto3==1.26.142 boto3==1.34.127
botocore==1.29.142 botocore==1.34.127
gTTS==2.3.2 gTTS==2.5.1
moviepy==1.0.3 moviepy==1.0.3
playwright==1.34.0 playwright==1.44.0
praw==7.7.0 praw==7.7.1
prawcore~=2.3.0 prawcore~=2.3.0
requests==2.31.0 requests==2.32.3
rich==13.4.1 rich==13.7.1
toml==0.10.2 toml==0.10.2
translators==5.7.6 translators==5.9.2
pyttsx3==2.90 pyttsx3==2.90
Pillow==9.5.0 Pillow==10.3.0
tomlkit==0.11.8 tomlkit==0.12.5
Flask==2.3.3 Flask==3.0.3
clean-text==0.6.0 clean-text==0.6.0
unidecode==1.3.6 unidecode==1.3.8
spacy==3.5.3 spacy==3.7.5
torch==2.0.1 torch==2.3.1
transformers==4.29.2 transformers==4.41.2
ffmpeg-python==0.2.0 ffmpeg-python==0.2.0
elevenlabs==0.2.17 elevenlabs==1.3.0
yt-dlp==2023.7.6 yt-dlp==2024.5.27
numpy==1.26.4

@ -31,6 +31,7 @@ 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_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" } 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" } 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" }
channel_name = { optional = true, default = "Reddit Tales", example = "Reddit Stories", explanation = "Sets the channel name for the video" }
[settings.background] [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 = ["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" }
@ -44,7 +45,7 @@ background_thumbnail_font_color = { optional = true, default = "255,255,255", ex
[settings.tts] [settings.tts]
voice_choice = { optional = false, default = "tiktok", options = ["elevenlabs", "streamlabspolly", "tiktok", "googletranslate", "awspolly", "pyttsx", ], example = "tiktok", explanation = "The voice platform used for TTS generation. " } 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, type = "bool", options = [true, false,], explanation = "Randomizes the voice used for each comment" } random_voice = { optional = false, type = "bool", 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_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" } 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" } aws_polly_voice = { optional = false, default = "Matthew", example = "Matthew", explanation = "The voice used for AWS Polly" }

@ -1,6 +1,6 @@
import numpy as np import numpy as np
from transformers import AutoTokenizer, AutoModel
import torch import torch
from transformers import AutoModel, AutoTokenizer
# Mean Pooling - Take attention mask into account for correct averaging # Mean Pooling - Take attention mask into account for correct averaging

@ -1,6 +1,6 @@
import os import os
from os.path import exists
import shutil import shutil
from os.path import exists
def _listdir(d): # listdir with full path def _listdir(d): # listdir with full path

@ -1,7 +1,8 @@
import zipfile
import requests
import os import os
import subprocess import subprocess
import zipfile
import requests
def ffmpeg_install_windows(): def ffmpeg_install_windows():
@ -39,7 +40,10 @@ def ffmpeg_install_windows():
# Rename and move files # Rename and move files
os.rename(f"{ffmpeg_extracted_folder}-6.0-full_build", ffmpeg_extracted_folder) os.rename(f"{ffmpeg_extracted_folder}-6.0-full_build", ffmpeg_extracted_folder)
for file in os.listdir(os.path.join(ffmpeg_extracted_folder, "bin")): for file in os.listdir(os.path.join(ffmpeg_extracted_folder, "bin")):
os.rename(os.path.join(ffmpeg_extracted_folder, "bin", file), os.path.join(".", file)) os.rename(
os.path.join(ffmpeg_extracted_folder, "bin", file),
os.path.join(".", file),
)
os.rmdir(os.path.join(ffmpeg_extracted_folder, "bin")) os.rmdir(os.path.join(ffmpeg_extracted_folder, "bin"))
for file in os.listdir(os.path.join(ffmpeg_extracted_folder, "doc")): for file in os.listdir(os.path.join(ffmpeg_extracted_folder, "doc")):
os.remove(os.path.join(ffmpeg_extracted_folder, "doc", file)) os.remove(os.path.join(ffmpeg_extracted_folder, "doc", file))
@ -101,7 +105,10 @@ def ffmpeg_install():
try: try:
# Try to run the FFmpeg command # Try to run the FFmpeg command
subprocess.run( subprocess.run(
["ffmpeg", "-version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ["ffmpeg", "-version"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
) )
except FileNotFoundError as e: except FileNotFoundError as e:
# Check if there's ffmpeg.exe in the current directory # Check if there's ffmpeg.exe in the current directory

@ -0,0 +1,13 @@
from PIL.ImageFont import FreeTypeFont, ImageFont
def getsize(font: ImageFont | FreeTypeFont, text: str):
left, top, right, bottom = font.getbbox(text)
width = right - left
height = bottom - top
return width, height
def getheight(font: ImageFont | FreeTypeFont, text: str):
_, height = getsize(font, text)
return height

@ -1,10 +1,12 @@
import os
import re import re
import textwrap import textwrap
import os
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from rich.progress import track from rich.progress import track
from TTS.engine_wrapper import process_text from TTS.engine_wrapper import process_text
from utils.fonts import getheight, getsize
def draw_multiple_line_text( def draw_multiple_line_text(
@ -14,12 +16,12 @@ def draw_multiple_line_text(
Draw multiline text over given image Draw multiline text over given image
""" """
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
Fontperm = font.getsize(text) font_height = getheight(font, text)
image_width, image_height = image.size image_width, image_height = image.size
lines = textwrap.wrap(text, width=wrap) lines = textwrap.wrap(text, width=wrap)
y = (image_height / 2) - (((Fontperm[1] + (len(lines) * padding) / len(lines)) * len(lines)) / 2) y = (image_height / 2) - (((font_height + (len(lines) * padding) / len(lines)) * len(lines)) / 2)
for line in lines: for line in lines:
line_width, line_height = font.getsize(line) line_width, line_height = getsize(font, line)
if transparent: if transparent:
shadowcolor = "black" shadowcolor = "black"
for i in range(1, 5): for i in range(1, 5):
@ -55,25 +57,17 @@ def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) ->
""" """
Render Images for video Render Images for video
""" """
title = process_text(reddit_obj["thread_title"], False)
texts = reddit_obj["thread_post"] texts = reddit_obj["thread_post"]
id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"]) id = re.sub(r"[^\w\s-]", "", reddit_obj["thread_id"])
if transparent: if transparent:
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100) font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100)
tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100)
else: else:
tfont = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 100) # for title
font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 100) font = ImageFont.truetype(os.path.join("fonts", "Roboto-Regular.ttf"), 100)
size = (1920, 1080) size = (1920, 1080)
image = Image.new("RGBA", size, theme) image = Image.new("RGBA", size, theme)
# for title
draw_multiple_line_text(image, title, tfont, txtclr, padding, wrap=30, transparent=transparent)
image.save(f"assets/temp/{id}/png/title.png")
for idx, text in track(enumerate(texts), "Rendering Image"): for idx, text in track(enumerate(texts), "Rendering Image"):
image = Image.new("RGBA", size, theme) image = Image.new("RGBA", size, theme)
text = process_text(text, False) text = process_text(text, False)

@ -1,6 +1,7 @@
import re import re
from typing import Tuple, Dict
from pathlib import Path from pathlib import Path
from typing import Dict, Tuple
import toml import toml
from rich.console import Console from rich.console import Console

@ -2,8 +2,8 @@ import json
from os.path import exists from os.path import exists
from utils import settings from utils import settings
from utils.console import print_substep
from utils.ai_methods import sort_by_similarity from utils.ai_methods import sort_by_similarity
from utils.console import print_substep
def get_subreddit_undone(submissions: list, subreddit, times_checked=0, similarity_scores=None): def get_subreddit_undone(submissions: list, subreddit, times_checked=0, similarity_scores=None):

@ -4,10 +4,10 @@ import time as pytime
from datetime import datetime from datetime import datetime
from time import sleep from time import sleep
from cleantext import clean
from requests import Response from requests import Response
from utils import settings from utils import settings
from cleantext import clean
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
from datetime import timezone from datetime import timezone

@ -3,13 +3,14 @@ import random
import re import re
from pathlib import Path from pathlib import Path
from random import randrange from random import randrange
from typing import Any, Tuple, Dict from typing import Any, Dict, Tuple
from moviepy.editor import VideoFileClip, AudioFileClip import yt_dlp
from moviepy.editor import AudioFileClip, VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from utils import settings from utils import settings
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
import yt_dlp
def load_background_options(): def load_background_options():

@ -1,25 +1,26 @@
import multiprocessing import multiprocessing
import os import os
import re import re
import tempfile
import textwrap
import threading
import time
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 pathlib import Path
from typing import Tuple, Any, Dict from typing import Dict, Final, Tuple
import ffmpeg import ffmpeg
import translators import translators
from PIL import Image from PIL import Image, ImageDraw, ImageFont
from rich.console import Console from rich.console import Console
from rich.progress import track from rich.progress import track
from utils import settings
from utils.cleanup import cleanup from utils.cleanup import cleanup
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
from utils.fonts import getheight
from utils.thumbnail import create_thumbnail from utils.thumbnail import create_thumbnail
from utils.videos import save_data from utils.videos import save_data
from utils import settings
import tempfile
import threading
import time
console = Console() console = Console()
@ -107,6 +108,63 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str:
return output_path return output_path
def create_fancy_thumbnail(image, text, text_color, padding, wrap=35):
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)
username_font = ImageFont.truetype(os.path.join("fonts", "Roboto-Bold.ttf"), 30)
draw.text(
(205, 825),
settings.config["settings"]["channel_name"],
font=username_font,
fill=text_color,
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
def merge_background_audio(audio: ffmpeg, reddit_id: str): def merge_background_audio(audio: ffmpeg, reddit_id: str):
"""Gather an audio and merge with assets/backgrounds/background.mp3 """Gather an audio and merge with assets/backgrounds/background.mp3
Args: Args:
@ -202,6 +260,23 @@ def make_final_video(
image_clips = list() image_clips = list()
Path(f"assets/temp/{reddit_id}/png").mkdir(parents=True, exist_ok=True)
# Credits to tim (beingbored)
# get the title_template image and draw a text in the middle part of it with the title of the thread
title_template = Image.open("assets/title_template.png")
title = reddit_obj["thread_title"]
title = name_normalize(title)
font_color = "#000000"
padding = 5
# create_fancy_thumbnail(image, text, text_color, padding
title_img = create_fancy_thumbnail(title_template, title, font_color, padding)
title_img.save(f"assets/temp/{reddit_id}/png/title.png")
image_clips.insert( image_clips.insert(
0, 0,
ffmpeg.input(f"assets/temp/{reddit_id}/png/title.png")["v"].filter( ffmpeg.input(f"assets/temp/{reddit_id}/png/title.png")["v"].filter(
@ -257,6 +332,9 @@ def make_final_video(
) )
) )
image_overlay = image_clips[i].filter("colorchannelmixer", aa=opacity) image_overlay = image_clips[i].filter("colorchannelmixer", aa=opacity)
assert (
audio_clips_durations is not None
), "Please make a GitHub issue if you see this. Ping @JasonLovesDoggo on GitHub."
background_clip = background_clip.overlay( background_clip = background_clip.overlay(
image_overlay, image_overlay,
enable=f"between(t,{current_time},{current_time + audio_clips_durations[i]})", enable=f"between(t,{current_time},{current_time + audio_clips_durations[i]})",

@ -4,7 +4,6 @@ from pathlib import Path
from typing import Dict, Final from typing import Dict, Final
import translators import translators
from playwright.async_api import async_playwright # pylint: disable=unused-import
from playwright.sync_api import ViewportSize, sync_playwright from playwright.sync_api import ViewportSize, sync_playwright
from rich.progress import track from rich.progress import track
@ -12,10 +11,9 @@ from utils import settings
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
from utils.imagenarator import imagemaker from utils.imagenarator import imagemaker
from utils.playwright import clear_cookie_by_name from utils.playwright import clear_cookie_by_name
from utils.videos import save_data from utils.videos import save_data
__all__ = ["download_screenshots_of_reddit_posts"] __all__ = ["get_screenshots_of_reddit_posts"]
def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int): def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
@ -60,6 +58,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
bgcolor = (255, 255, 255, 255) bgcolor = (255, 255, 255, 255)
txtcolor = (0, 0, 0) txtcolor = (0, 0, 0)
transparent = False transparent = False
if storymode and settings.config["settings"]["storymodemethod"] == 1: if storymode and settings.config["settings"]["storymodemethod"] == 1:
# for idx,item in enumerate(reddit_object["thread_post"]): # for idx,item in enumerate(reddit_object["thread_post"]):
print_substep("Generating images...") print_substep("Generating images...")
@ -87,6 +86,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
color_scheme="dark", color_scheme="dark",
viewport=ViewportSize(width=W, height=H), viewport=ViewportSize(width=W, height=H),
device_scale_factor=dsf, 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",
) )
cookies = json.load(cookie_file) cookies = json.load(cookie_file)
cookie_file.close() cookie_file.close()
@ -100,9 +100,9 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
page.set_viewport_size(ViewportSize(width=1920, height=1080)) page.set_viewport_size(ViewportSize(width=1920, height=1080))
page.wait_for_load_state() page.wait_for_load_state()
page.locator('[name="username"]').fill(settings.config["reddit"]["creds"]["username"]) page.locator(f'input[name="username"]').fill(settings.config["reddit"]["creds"]["username"])
page.locator('[name="password"]').fill(settings.config["reddit"]["creds"]["password"]) page.locator(f'input[name="password"]').fill(settings.config["reddit"]["creds"]["password"])
page.locator("button[class$='m-full-width']").click() page.get_by_role("button", name="Log In").click()
page.wait_for_timeout(5000) page.wait_for_timeout(5000)
login_error_div = page.locator(".AnimatedForm__errorMessage").first login_error_div = page.locator(".AnimatedForm__errorMessage").first
@ -220,7 +220,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
if page.locator('[data-testid="content-gate"]').is_visible(): if page.locator('[data-testid="content-gate"]').is_visible():
page.locator('[data-testid="content-gate"] button').click() page.locator('[data-testid="content-gate"] button').click()
page.goto(f'https://reddit.com{comment["comment_url"]}', timeout=0) page.goto(f"https://new.reddit.com/{comment['comment_url']}")
# translate code # translate code

@ -2,15 +2,15 @@ from typing import Tuple
from rich.console import Console from rich.console import Console
from TTS.GTTS import GTTS
from TTS.TikTok import TikTok
from TTS.aws_polly import AWSPolly from TTS.aws_polly import AWSPolly
from TTS.elevenlabs import elevenlabs
from TTS.engine_wrapper import TTSEngine from TTS.engine_wrapper import TTSEngine
from TTS.GTTS import GTTS
from TTS.pyttsx import pyttsx from TTS.pyttsx import pyttsx
from TTS.elevenlabs import elevenlabs
from TTS.streamlabs_polly import StreamlabsPolly from TTS.streamlabs_polly import StreamlabsPolly
from TTS.TikTok import TikTok
from utils import settings from utils import settings
from utils.console import print_table, print_step from utils.console import print_step, print_table
console = Console() console = Console()

Loading…
Cancel
Save