Added several features to the bot

New Features
- Change accent
- Change background video (random list)
- Change video speed (default voice is really slow)
- Better naming scheme for output videos in `final` folder
pull/253/head
satya ratnam 3 years ago
parent 6fc5d2a737
commit f4245ec02f

@ -3,8 +3,12 @@ REDDIT_CLIENT_SECRET=""
REDDIT_USERNAME="" REDDIT_USERNAME=""
REDDIT_PASSWORD="" REDDIT_PASSWORD=""
# You can follow the steps here "https://www.wikihow.com/Install-FFmpeg-on-Windows" to install FFmpeg on Windows.
# Alternatively you could download it from here https://ffmpeg.org/download.html
FFMPEG_PATH=""
# Valid options are "yes" and "no" for the variable below # Valid options are "yes" and "no" for the variable below
REDDIT_2FA="" REDDIT_2FA="no"
SUBREDDIT="" SUBREDDIT=""

1
.gitignore vendored

@ -1,4 +1,5 @@
assets/ assets/
final/
.env .env
reddit-bot-351418-5560ebc49cac.json reddit-bot-351418-5560ebc49cac.json
__pycache__ __pycache__

@ -1,10 +1,19 @@
# Reddit Video Maker Bot 🎥 # Reddit Video Maker Bot ++🎥
https://user-images.githubusercontent.com/6053155/170525726-2db23ae0-97b8-4bd1-8c95-00da60ce099f.mp4 https://user-images.githubusercontent.com/6053155/170525726-2db23ae0-97b8-4bd1-8c95-00da60ce099f.mp4
All done WITHOUT video editing or asset compiling. Just pure ✨programming magic✨. All done WITHOUT video editing or asset compiling. Just pure ✨programming magic✨.
Created by Lewis Menelaws & [TMRRW](https://tmrrwinc.ca) Created by Lewis Menelaws & [TMRRW](https://tmrrwinc.ca) modified by [Tya](https://tya.design/)
This is my first time using python and I tried adding a couple features.
## New Features✨
- Change [accent](https://gtts.readthedocs.io/en/latest/module.html?highlight=accent)
- Change background video (random list)
- Change video speed (default voice is really slow)
- Better naming scheme for output videos in `final` folder
[<picture> [<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/6053155/170528535-e274dc0b-7972-4b27-af22-637f8c370133.png"> <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/6053155/170528535-e274dc0b-7972-4b27-af22-637f8c370133.png">
@ -32,11 +41,13 @@ These videos on TikTok, YouTube and Instagram get MILLIONS of views across all p
1. Clone this repository 1. Clone this repository
2. Rename `.env.template` to `.env` and replace all values with the appropriate fields. To get Reddit keys (**required**), visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps) TL;DR set up an app that is a "script". Copy your keys into the `.env` file, along with whether your account uses two-factor authentication. 2. Rename `.env.template` to `.env` and replace all values with the appropriate fields. To get Reddit keys (**required**), visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps) TL;DR set up an app that is a "script". Copy your keys into the `.env` file, along with whether your account uses two-factor authentication.
3. Run `pip3 install -r requirements.txt` 3. Install [FFmpeg](https://ffmpeg.org/download.html) and include the path of the `ffmpeg.exe` into the `.env` file. A guide for installing FFmpeg on windows can be found [here](https://www.wikihow.com/Install-FFmpeg-on-Windows)
4. Run `playwright install` and `playwright install-deps`. 4. Run `pip3 install -r requirements.txt`
5. Run `python3 main.py` 5. Run `playwright install` and `playwright install-deps`.
6. ... 6. Make any configurations you want to change in the `main.py` file
7. Enjoy 😎 7. Run `python3 main.py`
8. ...
9. Enjoy 😎
## Contributing & Ways to improve 📈 ## Contributing & Ways to improve 📈
@ -45,7 +56,7 @@ In its current state, this bot does exactly what it needs to do. However, lots o
I have tried to simplify the code so anyone can read it and start contributing at any skill level. Don't be shy :) contribute! I have tried to simplify the code so anyone can read it and start contributing at any skill level. Don't be shy :) contribute!
- [ ] Allowing users to choose a reddit thread instead of being randomized. - [ ] Allowing users to choose a reddit thread instead of being randomized.
- [ ] Allowing users to choose a background that is picked instead of the Minecraft one. - [x] Allowing users to choose a background that is picked instead of the Minecraft one.
- [x] Allowing users to choose between any subreddit. - [x] Allowing users to choose between any subreddit.
- [ ] Allowing users to change voice. - [x] Allowing users to change voice.
- [ ] Creating better documentation and adding a command line interface. - [ ] Creating better documentation and adding a command line interface.

@ -1,4 +1,6 @@
from unittest.util import _MAX_LENGTH
from utils.console import print_markdown from utils.console import print_markdown
from utils.console import print_step
import time import time
from reddit.subreddit import get_subreddit_threads from reddit.subreddit import get_subreddit_threads
@ -6,18 +8,38 @@ from video_creation.background import download_background, chop_background_video
from video_creation.voices import save_text_to_mp3 from video_creation.voices import save_text_to_mp3
from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts
from video_creation.final_video import make_final_video from video_creation.final_video import make_final_video
import random
print_markdown( 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." "### 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. \n ### This modified version of this tool was created by [@tya.design](https://tya.design) and is available on [GitHub](https://github.com/Tyaaa-aa/RedditVideoMakerBot)."
) )
time.sleep(3) time.sleep(3)
# The maximum length of the video in seconds. (This is the length of the video that will be created, but not 100% accurate)
MAX_LENGTH = 1
# Video speed multiplier (1.0 = normal speed, 2.0 = double speed, 0.5 = half speed)
VIDEO_SPEED = 1.4
# Voice accent https://gtts.readthedocs.io/en/latest/module.html?highlight=accent
VOICE_ACCENT = "co.uk"
# Background video to use (video id from youtube)
# Use an array of videos to create a random background video
BACKGROUND_VIDEO_ARRAY = ["5sYdvjXX7YU", "Pt5_GSKIWQM"]
# select random 1
BACKGROUND_VIDEO = BACKGROUND_VIDEO_ARRAY[random.randrange(0, len(BACKGROUND_VIDEO_ARRAY))]
reddit_object = get_subreddit_threads() reddit_object = get_subreddit_threads()
video_title = reddit_object["thread_title"]
chosen_subreddit = reddit_object["subreddit"]
ffmpeg_exe = reddit_object["ffmpeg_exe"]
length, number_of_comments = save_text_to_mp3(reddit_object) length, number_of_comments = save_text_to_mp3(reddit_object, MAX_LENGTH, VOICE_ACCENT)
download_screenshots_of_reddit_posts(reddit_object, number_of_comments) download_screenshots_of_reddit_posts(reddit_object, number_of_comments)
download_background() download_background(BACKGROUND_VIDEO)
chop_background_video(length) chop_background_video(BACKGROUND_VIDEO, length)
final_video = make_final_video(number_of_comments) final_video = make_final_video(number_of_comments, chosen_subreddit, video_title, ffmpeg_exe, VIDEO_SPEED)

@ -3,6 +3,7 @@ import praw
import random import random
from dotenv import load_dotenv from dotenv import load_dotenv
import os import os
import sys
def get_subreddit_threads(): def get_subreddit_threads():
@ -13,7 +14,17 @@ def get_subreddit_threads():
load_dotenv() load_dotenv()
print_step("Getting AskReddit threads...") # print_step("Getting AskReddit threads...")
if os.getenv("FFMPEG_PATH"):
ffmpeg_exe = os.getenv("FFMPEG_PATH")
else:
# ! Prompt the user to enter the path to FFmpeg
try:
ffmpeg_exe = input("What is the path to ffmpeg.exe?")
except ValueError:
print_step("Error with FFmpeg path. Terminating script.", style="bold red")
sys.exit()
if os.getenv("REDDIT_2FA").lower() == "yes": if os.getenv("REDDIT_2FA").lower() == "yes":
print( print(
@ -38,24 +49,34 @@ def get_subreddit_threads():
if os.getenv("SUBREDDIT"): if os.getenv("SUBREDDIT"):
subreddit = reddit.subreddit(os.getenv("SUBREDDIT")) subreddit = reddit.subreddit(os.getenv("SUBREDDIT"))
subreddit_title = os.getenv("SUBREDDIT")
else: else:
# ! Prompt the user to enter a subreddit # ! Prompt the user to enter a subreddit
try: try:
subreddit = reddit.subreddit( subreddit_title = input("What subreddit would you like to pull from? ")
input("What subreddit would you like to pull from? ") subreddit = reddit.subreddit(subreddit_title)
)
except ValueError: except ValueError:
subreddit = reddit.subreddit("askreddit") subreddit = reddit.subreddit("askreddit")
subreddit_title = "askreddit"
print_substep("Subreddit not defined. Using AskReddit.") print_substep("Subreddit not defined. Using AskReddit.")
threads = subreddit.hot(limit=25) threads = subreddit.hot(limit=25)
submission = list(threads)[random.randrange(0, 25)] submission = list(threads)[random.randrange(0, 25)]
print_substep(f"Video will be: {submission.title} :thumbsup:")
# print_substep(f"Video will be: {submission.title}")
print_step("Video will be: "+subreddit_title.upper()+" - "+submission.title)
# print_step("Thread Title: " + submission.title)
try: try:
content["thread_url"] = submission.url content["thread_url"] = submission.url
content["thread_title"] = submission.title content["thread_title"] = submission.title
content["comments"] = [] content["comments"] = []
content["subreddit"] = subreddit_title.upper()
content["ffmpeg_exe"] = str(ffmpeg_exe)
for top_level_comment in submission.comments: for top_level_comment in submission.comments:
content["comments"].append( content["comments"].append(

@ -7,43 +7,38 @@ from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from moviepy.editor import VideoFileClip from moviepy.editor import VideoFileClip
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
def get_start_and_end_times(video_length, length_of_clip): def get_start_and_end_times(video_length, length_of_clip):
random_time = randrange(180, int(length_of_clip) - int(video_length)) random_time = randrange(180, int(length_of_clip) - int(video_length))
return random_time, random_time + video_length return random_time, random_time + video_length
def download_background(): def download_background(youtube_id):
"""Downloads the background video from youtube. if not Path(f"assets/mp4/background-{youtube_id}.mp4").is_file():
Shoutout to: bbswitzer (https://www.youtube.com/watch?v=n_Dv4JMiwK8)
"""
if not Path("assets/mp4/background.mp4").is_file():
print_step( print_step(
"We need to download the Minecraft background video. This is fairly large but it's only done once." "We need to download the background video. This is fairly large but it's only done once."
) )
print_substep("Downloading the background video... please be patient.") print_substep("Downloading the background video... please be patient.")
ydl_opts = { ydl_opts = {
"outtmpl": "assets/mp4/background.mp4", "outtmpl": f"assets/mp4/background-{youtube_id}.mp4",
"merge_output_format": "mp4", "merge_output_format": "mp4",
} }
with YoutubeDL(ydl_opts) as ydl: with YoutubeDL(ydl_opts) as ydl:
ydl.download("https://www.youtube.com/watch?v=n_Dv4JMiwK8") ydl.download(f"https://www.youtube.com/watch?v={youtube_id}")
print_substep("Background video downloaded successfully!", style="bold green") print_substep("Background video downloaded successfully!", style="bold green")
def chop_background_video(video_length): def chop_background_video(youtube_id, video_length):
print_step("Finding a spot in the background video to chop...") print_step("Finding a spot in the background video to chop...")
background = VideoFileClip("assets/mp4/background.mp4") background_path = f"assets/mp4/background-{youtube_id}.mp4"
background = VideoFileClip(background_path)
start_time, end_time = get_start_and_end_times(video_length, background.duration) start_time, end_time = get_start_and_end_times(video_length, background.duration)
ffmpeg_extract_subclip( ffmpeg_extract_subclip(
"assets/mp4/background.mp4", background_path,
start_time, start_time,
end_time, end_time,
targetname="assets/mp4/clip.mp4", targetname="assets/mp4/clip.mp4",

@ -8,12 +8,16 @@ from moviepy.editor import (
CompositeVideoClip, CompositeVideoClip,
) )
from utils.console import print_step from utils.console import print_step
import re
from pathlib import Path
import subprocess
from typing import List
W, H = 1080, 1920 W, H = 1080, 1920
def make_final_video(number_of_clips): def make_final_video(number_of_clips, chosen_subreddit, video_title, ffmpeg_exe, video_speed):
print_step("Creating the final video...") print_step("Creating the final video...")
VideoFileClip.reW = lambda clip: clip.resize(width=W) VideoFileClip.reW = lambda clip: clip.resize(width=W)
VideoFileClip.reH = lambda clip: clip.resize(width=H) VideoFileClip.reH = lambda clip: clip.resize(width=H)
@ -54,8 +58,73 @@ def make_final_video(number_of_clips):
image_concat.audio = audio_composite image_concat.audio = audio_composite
final = CompositeVideoClip([background_clip, image_concat]) final = CompositeVideoClip([background_clip, image_concat])
final.write_videofile( final.write_videofile(
"assets/final_video.mp4", fps=30, audio_codec="aac", audio_bitrate="192k" "assets/final_video_raw.mp4", fps=30, audio_codec="aac", audio_bitrate="192k"
) )
videos = [
Video(speed=video_speed, path="assets/final_video_raw.mp4"),
]
print_step("Creating sped up version of the final video...")
Path("final/").mkdir(parents=True, exist_ok=True)
final_video_title = urlify(chosen_subreddit)+"-"+urlify(video_title)+".mp4"
concatenate_videos(ffmpeg_exe,
videos=videos, output_file=f"final/"+final_video_title)
for i in range(0, number_of_clips): for i in range(0, number_of_clips):
pass pass
class Video():
def __init__(self, path: str, speed: float = 1.0):
self.path = path
self.speed = speed
def concatenate_videos(ffmpeg_exe, videos: List[Video], output_file: str):
COMMAND_BASE = [ffmpeg_exe]
COMMAND_BASE += ["-n"] # disable file overwriting
video_count = len(videos)
video_speeds = [float(1/x.speed) for x in videos]
audio_speeds = [float(x.speed) for x in videos]
cmd_input_files = []
filters, concat = ("", "")
for i, x in enumerate(videos):
cmd_input_files += ["-i", x.path]
filters += f"[{i}:v] setpts = {video_speeds[i]} * PTS [v{i}];"
filters += f"[{i}:a] atempo = {audio_speeds[i]} [a{i}];"
concat += f"[v{i}][a{i}]"
concat += f"concat = n = {video_count}:v = 1:a = 1 [v_all][a_all]"
filter_complex = f"{filters}{concat}".replace(" ", "")
cmd_filter_complex = [
"-filter_complex", filter_complex,
]
cmd_map = [
"-map", "[v_all]",
"-map", "[a_all]",
]
command = sum([
COMMAND_BASE,
cmd_input_files,
cmd_filter_complex,
cmd_map,
[output_file],
], [])
subprocess.run(command)
def urlify(s):
# Remove all non-word characters (everything except numbers and letters)
s = re.sub(r"[^\w\s]", '', s)
# Replace all runs of whitespace with a single dash
s = re.sub(r"\s+", '-', s)
return s

@ -5,7 +5,7 @@ from utils.console import print_step, print_substep
from rich.progress import track from rich.progress import track
def save_text_to_mp3(reddit_obj): def save_text_to_mp3(reddit_obj, maxlength, accent):
"""Saves Text to MP3 files. """Saves Text to MP3 files.
Args: Args:
@ -17,15 +17,15 @@ def save_text_to_mp3(reddit_obj):
# Create a folder for the mp3 files. # Create a folder for the mp3 files.
Path("assets/mp3").mkdir(parents=True, exist_ok=True) Path("assets/mp3").mkdir(parents=True, exist_ok=True)
tts = gTTS(text=reddit_obj["thread_title"], lang="en", slow=False) tts = gTTS(text=reddit_obj["thread_title"], lang="en", tld=accent, slow=False)
tts.save(f"assets/mp3/title.mp3") tts.save(f"assets/mp3/title.mp3")
length += MP3(f"assets/mp3/title.mp3").info.length length += MP3(f"assets/mp3/title.mp3").info.length
for idx, comment in track(enumerate(reddit_obj["comments"]), "Saving..."): for idx, comment in track(enumerate(reddit_obj["comments"]), "Saving..."):
# ! Stop creating mp3 files if the length is greater than 50 seconds. This can be longer, but this is just a good starting point # ! Stop creating mp3 files if the length is greater than 50 seconds. This can be longer, but this is just a good starting point
if length > 50: if length > maxlength:
break break
tts = gTTS(text=comment["comment_body"], lang="en", slow=False) tts = gTTS(text=comment["comment_body"], lang="en", tld=accent, slow=False)
tts.save(f"assets/mp3/{idx}.mp3") tts.save(f"assets/mp3/{idx}.mp3")
length += MP3(f"assets/mp3/{idx}.mp3").info.length length += MP3(f"assets/mp3/{idx}.mp3").info.length

Loading…
Cancel
Save