Merge branch 'develop' into master

pull/2047/head
Jason Cameron 1 year ago committed by GitHub
commit 207a375c6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

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

@ -6,7 +6,10 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: psf/black@stable
with:
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
.idea/caches/build_file_checksums.ser
assets/
assets/temp
assets/backgrounds
/.vscode
out
.DS_Store
@ -244,4 +245,4 @@ video_creation/data/videos.json
video_creation/data/envvars.txt
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-get install -y ffmpeg
@ -9,8 +9,4 @@ ADD . /app
WORKDIR /app
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"]

@ -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
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
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.
@ -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.
### For any questions or support join the [Discord](https://discord.gg/Vkanmh6C8V) server
### For any questions or support join the [Discord](https://discord.gg/qfQSx45xCV) server
## Developers and maintainers.
@ -101,6 +101,8 @@ Freebiell (Freebie#3263) - https://github.com/FreebieII
Aman Raza (electro199#8130) - https://github.com/electro199
Cyteon (cyteon) - https://github.com/cyteon
## LICENSE
[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 random
import time
from typing import Optional, Final
from typing import Final, Optional
import requests

@ -1,33 +1,28 @@
import random
from elevenlabs import generate, save
from elevenlabs import save
from elevenlabs.client import ElevenLabs
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
self.client: ElevenLabs = None
def run(self, text, filepath, random_voice: bool = False):
if self.client is None:
self.initialize()
if random_voice:
voice = self.randomvoice()
else:
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"]:
api_key = settings.config["settings"]["tts"]["elevenlabs_api_key"]
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."
)
audio = generate(api_key=api_key, text=text, voice=voice, model="eleven_multilingual_v1")
save(audio=audio, filename=filepath)
self.client = ElevenLabs(api_key=api_key)
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

@ -43,9 +43,11 @@ class StreamlabsPolly:
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()
body = {"voice": voice, "text": text, "service": "polly"}
headers = {"Referer": "https://streamlabs.com/"}
response = requests.post(self.url, headers=headers, data=body)
if not check_ratelimit(response):
self.run(text, filepath, random_voice)

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@ -9,8 +9,7 @@ from prawcore import ResponseException
from reddit.subreddit import get_subreddit_threads
from utils import settings
from utils.cleanup import cleanup
from utils.console import print_markdown, print_step
from utils.console import print_substep
from utils.console import print_markdown, print_step, print_substep
from utils.ffmpeg_install import ffmpeg_install
from utils.id import id
from utils.version import checkversion
@ -19,16 +18,16 @@ import argparse
#!/usr/bin/env python
from video_creation.background import (
download_background_video,
download_background_audio,
chop_background,
download_background_audio,
download_background_video,
get_background_config,
)
from video_creation.final_video import make_final_video
from video_creation.screenshot_downloader import get_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3
__VERSION__ = "3.2.1"
__VERSION__ = "3.3.0"
print(
"""
@ -40,7 +39,6 @@ print(
"""
)
# Modified by JasonLovesDoggo
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/"
)
@ -88,7 +86,6 @@ if __name__ == "__main__":
parser.add_argument("--run-many", type=int, help="Run the program multiple times")
parser.add_argument("--id")
args = parser.parse_args()
if sys.version_info.major != 3 or sys.version_info.minor not in [10, 11]:
print(
"Hey! Congratulations, you've made it so far (which is pretty rare with no Python 3.10). Unfortunately, this program only works on Python 3.10. Please install Python 3.10 and try again."

@ -104,7 +104,7 @@ def get_subreddit_threads(POST_ID: str):
upvotes = submission.score
ratio = submission.upvote_ratio * 100
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"Thread url is: {threadurl} :thumbsup:", style="bold green")

@ -1,23 +1,24 @@
boto3==1.26.142
botocore==1.29.142
boto3==1.34.127
botocore==1.34.127
gTTS==2.5.1
moviepy==1.0.3
playwright==1.34.0
praw==7.7.0
playwright==1.44.0
praw==7.7.1
prawcore~=2.3.0
requests==2.31.0
rich==13.4.1
requests==2.32.3
rich==13.7.1
toml==0.10.2
translators==5.9.1
translators==5.9.2
pyttsx3==2.90
Pillow==10.3.0
tomlkit==0.11.8
Flask==2.3.3
tomlkit==0.12.5
Flask==3.0.3
clean-text==0.6.0
unidecode==1.3.8
spacy==3.5.3
torch==2.3.0
transformers==4.40.2
spacy==3.7.5
torch==2.3.1
transformers==4.41.2
ffmpeg-python==0.2.0
elevenlabs==0.2.17
yt-dlp==2023.7.6
elevenlabs==1.3.0
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_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" }
channel_name = { optional = true, default = "Reddit Tales", example = "Reddit Stories", explanation = "Sets the channel name for the video" }
[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" }

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

@ -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

@ -6,6 +6,7 @@ from PIL import Image, ImageDraw, ImageFont
from rich.progress import track
from TTS.engine_wrapper import process_text
from utils.fonts import getheight, getsize
def draw_multiple_line_text(
@ -15,12 +16,12 @@ def draw_multiple_line_text(
Draw multiline text over given image
"""
draw = ImageDraw.Draw(image)
Fontperm = font.getsize(text)
font_height = getheight(font, text)
image_width, image_height = image.size
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:
line_width, line_height = font.getsize(line)
line_width, line_height = getsize(font, line)
if transparent:
shadowcolor = "black"
for i in range(1, 5):
@ -56,25 +57,17 @@ def imagemaker(theme, reddit_obj: dict, txtclr, padding=5, transparent=False) ->
"""
Render Images for video
"""
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"), 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"), 100)
size = (1920, 1080)
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"):
image = Image.new("RGBA", size, theme)
text = process_text(text, False)

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

@ -3,10 +3,10 @@ import random
import re
from pathlib import Path
from random import randrange
from typing import Any, Tuple, Dict
from typing import Any, Dict, Tuple
import yt_dlp
from moviepy.editor import VideoFileClip, AudioFileClip
from moviepy.editor import AudioFileClip, VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from utils import settings

@ -2,21 +2,23 @@ import multiprocessing
import os
import re
import tempfile
import textwrap
import threading
import time
from os.path import exists # Needs to be imported specifically
from typing import Final
from typing import Tuple, Dict
from pathlib import Path
from typing import Dict, Final, Tuple
import ffmpeg
import translators
from PIL import Image
from PIL import Image, ImageDraw, ImageFont
from rich.console import Console
from rich.progress import track
from utils import settings
from utils.cleanup import cleanup
from utils.console import print_step, print_substep
from utils.fonts import getheight
from utils.thumbnail import create_thumbnail
from utils.videos import save_data
@ -106,6 +108,63 @@ def prepare_background(reddit_id: str, W: int, H: int) -> str:
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):
"""Gather an audio and merge with assets/backgrounds/background.mp3
Args:
@ -201,6 +260,23 @@ def make_final_video(
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(
0,
ffmpeg.input(f"assets/temp/{reddit_id}/png/title.png")["v"].filter(
@ -256,6 +332,9 @@ def make_final_video(
)
)
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(
image_overlay,
enable=f"between(t,{current_time},{current_time + audio_clips_durations[i]})",

@ -13,7 +13,7 @@ from utils.imagenarator import imagemaker
from utils.playwright import clear_cookie_by_name
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):
@ -58,6 +58,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
bgcolor = (255, 255, 255, 255)
txtcolor = (0, 0, 0)
transparent = False
if storymode and settings.config["settings"]["storymodemethod"] == 1:
# for idx,item in enumerate(reddit_object["thread_post"]):
print_substep("Generating images...")
@ -85,6 +86,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
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",
)
cookies = json.load(cookie_file)
cookie_file.close()
@ -98,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.wait_for_load_state()
page.locator('[name="username"]').fill(settings.config["reddit"]["creds"]["username"])
page.locator('[name="password"]').fill(settings.config["reddit"]["creds"]["password"])
page.locator("button[class$='m-full-width']").click()
page.locator(f'input[name="username"]').fill(settings.config["reddit"]["creds"]["username"])
page.locator(f'input[name="password"]').fill(settings.config["reddit"]["creds"]["password"])
page.get_by_role("button", name="Log In").click()
page.wait_for_timeout(5000)
login_error_div = page.locator(".AnimatedForm__errorMessage").first
@ -218,7 +220,7 @@ def get_screenshots_of_reddit_posts(reddit_object: dict, screenshot_num: int):
if page.locator('[data-testid="content-gate"]').is_visible():
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

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

Loading…
Cancel
Save