pull/418/head
Jason 3 years ago
parent 3c9bc3d0ca
commit f491479fb7

@ -14,14 +14,16 @@ Created by Lewis Menelaws & [TMRRW](https://tmrrwinc.ca)
## Motivation 🤔 ## Motivation 🤔
These videos on TikTok, YouTube and Instagram get MILLIONS of views across all platforms and require very little effort. The only original thing being done is the editing and gathering of all materials... These videos on TikTok, YouTube and Instagram get MILLIONS of views across all platforms and require very little effort.
The only original thing being done is the editing and gathering of all materials...
... but what if we can automate that process? 🤔 ... but what if we can automate that process? 🤔
## Disclaimers 🚨 ## Disclaimers 🚨
- This is purely for fun purposes. - This is purely for fun purposes.
- **At the moment**, this repository won't attempt to upload this content through this bot. It will give you a file that you will then have to upload manually. This is for the sake of avoiding any sort of community guideline issues. - **At the moment**, this repository won't attempt to upload this content through this bot. It will give you a file that
you will then have to upload manually. This is for the sake of avoiding any sort of community guideline issues.
## Requirements ## Requirements
@ -31,17 +33,21 @@ These videos on TikTok, YouTube and Instagram get MILLIONS of views across all p
## Installation 👩‍💻 ## Installation 👩‍💻
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. Run `pip3 install -r requirements.txt`
4. install [SoX](https://sourceforge.net/projects/sox/files/sox/) 4. install [SoX](https://sourceforge.net/projects/sox/files/sox/)
5. Run `playwright install` and `playwright install-deps`. 5. Run `playwright install` and `playwright install-deps`.
6. Run `python3 main.py` 6. Run `python3 main.py`
7. Enjoy 😎 7. Enjoy 😎
## Contributing & Ways to improve 📈 ## Contributing & Ways to improve 📈
In its current state, this bot does exactly what it needs to do. However, lots of improvements can be made. In its current state, this bot does exactly what it needs to do. However, lots of improvements can be made.
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!
- [ ] Creating better documentation and adding a command line interface. - [ ] Creating better documentation and adding a command line interface.
- [x] Allowing users to choose a reddit thread instead of being randomized. - [x] Allowing users to choose a reddit thread instead of being randomized.

@ -1,12 +1,17 @@
from utils.cleanup import cleanup import os
from utils.console import print_markdown
import time import time
from dotenv import load_dotenv
from reddit.subreddit import get_subreddit_threads from reddit.subreddit import get_subreddit_threads
from utils.cleanup import cleanup
from utils.console import print_markdown
from video_creation.background import download_background, chop_background_video from video_creation.background import download_background, chop_background_video
from video_creation.voices import save_text_to_mp3
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
from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts
from video_creation.voices import save_text_to_mp3
load_dotenv()
# base code by elebumm # base code by elebumm
print_markdown( print_markdown(
"### Thanks for using this tool! 😊 Feel free to contribute to this project on GitHub! (JasonLovesDoggo/RedditVideoMakerBot). If you have any questions, feel free to reach out to me on Twitter @JasonLovesDoggo or submit a GitHub issue.") "### Thanks for using this tool! 😊 Feel free to contribute to this project on GitHub! (JasonLovesDoggo/RedditVideoMakerBot). If you have any questions, feel free to reach out to me on Twitter @JasonLovesDoggo or submit a GitHub issue.")
@ -34,9 +39,17 @@ def main():
final_video = make_final_video(number_of_comments) final_video = make_final_video(number_of_comments)
def run_many(times):
for x in range(times):
main()
if __name__ == '__main__': if __name__ == '__main__':
try: try:
main() if os.getenv('TIMES_TO_RUN'):
run_many(int(os.getenv('TIMES_TO_RUN')))
else:
main()
except KeyboardInterrupt: except KeyboardInterrupt:
print_markdown("## Clearing temp files") print_markdown("## Clearing temp files")
cleanup() cleanup()

@ -1,11 +1,9 @@
import re
from utils.console import print_step, print_substep
import praw
import random import random
from dotenv import load_dotenv
from os import getenv, environ from os import getenv, environ
import praw
from utils.console import print_step, print_substep
from utils.videos import check_done from utils.videos import check_done
TEXT_WHITELIST = set('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890') TEXT_WHITELIST = set('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890')
@ -22,7 +20,6 @@ def get_subreddit_threads():
print_step("Getting subreddit threads...") print_step("Getting subreddit threads...")
content = {} content = {}
load_dotenv()
if getenv("REDDIT_2FA").casefold() == "yes": if getenv("REDDIT_2FA").casefold() == "yes":
print("\nEnter your two-factor authentication code from your authenticator app.\n") print("\nEnter your two-factor authentication code from your authenticator app.\n")
code = input("> ") code = input("> ")
@ -66,7 +63,7 @@ def get_subreddit_threads():
for top_level_comment in submission.comments: for top_level_comment in submission.comments:
content["comments"].append( content["comments"].append(
{"comment_body": top_level_comment.body, "comment_url": top_level_comment.permalink, {"comment_body": top_level_comment.body, "comment_url": top_level_comment.permalink,
"comment_id": top_level_comment.id, }) "comment_id": top_level_comment.id, })
except AttributeError as e: except AttributeError as e:
pass pass

@ -20,4 +20,3 @@ def cleanup() -> int:
os.remove('./assets/temp/mp3/' + file) os.remove('./assets/temp/mp3/' + file)
return count return count
return 0 return 0

@ -1,4 +1,3 @@
import inspect
import json import json
from os import getenv from os import getenv

@ -1,4 +1,6 @@
import requests, base64, random, os import base64
import random
import requests
# https://twitter.com/scanlime/status/1512598559769702406 # https://twitter.com/scanlime/status/1512598559769702406
voices = [ # DISNEY VOICES voices = [ # DISNEY VOICES
@ -74,4 +76,3 @@ class TTTTSWrapper: # TikTok Text-to-Speech Wrapper
if ok_or_good == 1: # 1/10 chance of ok voice if ok_or_good == 1: # 1/10 chance of ok voice
return random.choice(good_voices['ok']) return random.choice(good_voices['ok'])
return random.choice(good_voices['good']) # 9/10 chance of good voice return random.choice(good_voices['good']) # 9/10 chance of good voice

@ -1,11 +1,11 @@
import random import random
from os import listdir, environ from os import listdir, environ, remove
from pathlib import Path from pathlib import Path
from random import randrange from random import randrange
from pytube import YouTube
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from moviepy.editor import VideoFileClip from moviepy.editor import VideoFileClip
from rich.progress import Progress from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from yt_dlp import YoutubeDL
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
@ -30,27 +30,31 @@ def download_background():
background_options): # if there are any background videos not installed background_options): # if there are any background videos not installed
print_step("We need to download the backgnrounds videos. they are fairly large but it's only done once. 😎") print_step("We need to download the backgnrounds videos. they are fairly large but it's only done once. 😎")
print_substep("Downloading the backgrounds videos... please be patient 🙏 ") print_substep("Downloading the backgrounds videos... please be patient 🙏 ")
with Progress() as progress:
download_task = progress.add_task("[green]Downloading...", total=2) for uri, filename, credit in background_options:
filename = f"{credit}-{filename}"
for uri, filename, credit in background_options: ydl_opts = {'outtmpl': f'assets/backgrounds/_raw_{filename}', 'merge_output_format': 'mp4', }
print_substep(f"Downloading {filename} from {uri}") with YoutubeDL(ydl_opts) as ydl:
YouTube(uri).streams.filter(res="720p").first().download("assets/backgrounds", ydl.download(uri)
filename=f"{credit}-{filename}") videoclip = VideoFileClip(f"assets/backgrounds/{filename}")
progress.update(download_task, advance=1) new_clip = videoclip.without_audio()
new_clip.write_videofile(f"assets/backgrounds/{filename}")
remove(f'assets/backgrounds/_raw_{filename}')
print_substep("Background videos downloaded successfully! 🎉", style="bold green") print_substep("Background videos downloaded successfully! 🎉", style="bold green")
def chop_background_video(video_length): def chop_background_video(video_length):
print_step("Finding a spot in the backgrounds video to chop...✂️") print_step("Finding a spot in the background video to chop...")
choice = random.choice(listdir('assets/backgrounds')) choice = random.choice(listdir('assets/backgrounds'))
environ["background_credit"] = choice.split('-')[0] environ["background_credit"] = choice.split('-')[0]
background = VideoFileClip(f"assets/backgrounds/{choice}") background = VideoFileClip(f"assets/backgrounds/{choice}")
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(f'assets/backgrounds/{choice}', start_time, end_time, print_substep(choice)
targetname="assets/temp/backgrounds.mp4", ) ffmpeg_extract_subclip(
print_substep("Background video chopped successfully! 🎉", style="bold green") f"assets/backgrounds/{choice}",
start_time,
end_time,
targetname="assets/temp/background.mp4",
)
print_substep("Background video chopped successfully!", style="bold green")

@ -1,9 +1,9 @@
[ [
{ {
"id": "v2kd2q", "id": "v2kd2q",
"time": "1654128204", "time": "1654128204",
"background_credit": "bbswitzer", "background_credit": "bbswitzer",
"reddit_title": "What movie do you absolutely love yet acknowledge is not a super wellmade movie", "reddit_title": "What movie do you absolutely love yet acknowledge is not a super wellmade movie",
"filename": "What movie do you absolutely l....mp4" "filename": "What movie do you absolutely l....mp4"
} }
] ]

@ -1,12 +1,13 @@
import json import json
from os import getenv from os import getenv
from playwright.sync_api import sync_playwright
from pathlib import Path from pathlib import Path
from playwright.sync_api import sync_playwright, ViewportSize
from rich.progress import track from rich.progress import track
from utils.console import print_step, print_substep from utils.console import print_step, print_substep
def download_screenshots_of_reddit_posts(reddit_object, screenshot_num): def download_screenshots_of_reddit_posts(reddit_object, screenshot_num):
"""Downloads screenshots of reddit posts as they are seen on the web. """Downloads screenshots of reddit posts as they are seen on the web.
@ -31,15 +32,15 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num):
context.add_cookies(cookies) context.add_cookies(cookies)
# Get the thread screenshot # Get the thread screenshot
page = context.new_page() page = context.new_page()
page.set_viewport_size(ViewportSize(width=1920, height=1080))
page.goto(reddit_object["thread_url"]) page.goto(reddit_object["thread_url"])
if page.locator('[data-testid="content-gate"]').is_visible(): if page.locator('[data-testid="content-gate"]').is_visible():
# This means the post is NSFW and requires to click the proceed button. # This means the post is NSFW and requires to click the proceed button.
if getenv("ALLOW_NSFW").casefold() == "false": if getenv("ALLOW_NSFW").casefold() == "false":
print_substep("NSFW Post Detected. Skipping...") print_substep("NSFW Post Detected. Skipping...")
from subprocess import call from main import main
call(["python", "main.py"]) main()
exit(1)
print_substep("Post is NSFW. You are spicy... :fire:") print_substep("Post is NSFW. You are spicy... :fire:")
page.locator('[data-testid="content-gate"] button').click() page.locator('[data-testid="content-gate"] button').click()

@ -1,13 +1,11 @@
from os import remove
import sox
from pathlib import Path from pathlib import Path
import sox
from mutagen import MutagenError from mutagen import MutagenError
from mutagen.mp3 import MP3, HeaderNotFoundError from mutagen.mp3 import MP3, HeaderNotFoundError
from utils.console import print_step, print_substep
from rich.progress import track from rich.progress import track
from utils.console import print_step, print_substep
from video_creation.TTSwrapper import TTTTSWrapper from video_creation.TTSwrapper import TTTTSWrapper
VIDEO_LENGTH: int = 40 # secs VIDEO_LENGTH: int = 40 # secs
@ -29,11 +27,11 @@ def save_text_to_mp3(reddit_obj):
ttttsw.tts(reddit_obj["thread_title"], filename=f"assets/temp/mp3/title.mp3", random_speaker=True) ttttsw.tts(reddit_obj["thread_title"], filename=f"assets/temp/mp3/title.mp3", random_speaker=True)
try: try:
length += MP3(f"assets/temp/mp3/title.mp3").info.length length += MP3(f"assets/temp/mp3/title.mp3").info.length
except HeaderNotFoundError: except HeaderNotFoundError: # note to self AudioFileClip
length = sox.file_info.duration(f"assets/temp/mp3/title.mp3") length += sox.file_info.duration(f"assets/temp/mp3/title.mp3")
com = 0 com = 0
for comment in track((reddit_obj["comments"]), "Saving..."): for comment in track((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_voices starting point # ! Stop creating mp3 files if the length is greater than VIDEO_LENGTH seconds. This can be longer, but this is just a good_voices starting point
if length > VIDEO_LENGTH: if length > VIDEO_LENGTH:
break break
@ -49,8 +47,8 @@ def save_text_to_mp3(reddit_obj):
print('would have removed' print('would have removed'
f"assets/temp/mp3/{com}.mp3" f"assets/temp/mp3/{com}.mp3"
f"assets/temp/png/comment_{com}.png") f"assets/temp/png/comment_{com}.png")
#remove(f"assets/temp/mp3/{com}.mp3") # remove(f"assets/temp/mp3/{com}.mp3")
#remove(f"assets/temp/png/comment_{com}.png")# todo might cause odd un-syncing # remove(f"assets/temp/png/comment_{com}.png")# todo might cause odd un-syncing
print_substep("Saved Text to MP3 files Successfully.", style="bold green") print_substep("Saved Text to MP3 files Successfully.", style="bold green")
# ! Return the index so we know how many screenshots of comments we need to make. # ! Return the index so we know how many screenshots of comments we need to make.

Loading…
Cancel
Save