Merge pull request #560 from elebumm/develop

2.1
pull/599/head
Jason 2 years ago committed by GitHub
commit 8d54e373df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,3 @@
# This can be found in the email that reddit sent you when you created the app
REDDIT_CLIENT_ID=""
REDDIT_CLIENT_SECRET=""
@ -6,16 +5,8 @@ REDDIT_USERNAME=""
REDDIT_PASSWORD=""
# If no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no"
RANDOM_THREAD=""
RANDOM_THREAD="no"
# Filters the comments by range of length (min and max characters)
# Min has to be less or equal to max
# DO NOT INSERT ANY SPACES BETWEEN THE COMMA AND THE VALUES
COMMENT_LENGTH_RANGE = "min,max"
# The absolute path of the folder where you want to save the final video
# If empty or wrong, the path will be 'results/'
FINAL_VIDEO_PATH=""
# Valid options are "yes" and "no" for the variable below
REDDIT_2FA=""
SUBREDDIT="AskReddit"
@ -25,15 +16,16 @@ ALLOW_NSFW="False"
POST_ID=""
#set to either LIGHT or DARK
THEME="LIGHT"
# used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for once
# used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1
TIMES_TO_RUN=""
MAX_COMMENT_LENGTH="500"
# max number of characters a comment can have.
MAX_COMMENT_LENGTH="500" # default is 500
# Range is 0 -> 1 recommended around 0.8-0.9
OPACITY="1"
# see TTSwrapper.py for all valid options
# see different voice options: todo: add docs
VOICE="Matthew" # e.g. en_us_002
TTsChoice="polly" # todo add docs
TTsChoice="polly"
# IN-PROGRESS - not yet implemented
STORYMODE="False"

1
.gitattributes vendored

@ -0,0 +1 @@
* text=auto eol=lf

87
.gitignore vendored

@ -153,20 +153,91 @@ dmypy.json
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
assets/
out
.DS_Store
.setup-done-before
assets/
results/*
.env
reddit-bot-351418-5560ebc49cac.json
/.idea
*.pyc
/video_creation/data/videos.json
video_creation/data/videos.json
video_creation/data/envvars.txt

@ -39,21 +39,22 @@ The only original thing being done is the editing and gathering of all materials
## Installation 👩‍💻
1. Clone this repository
2. 2a **Automatic Install**: Run `python3 main.py` and type 'yes' to activate the setup assistant.
2. 2a **Automatic Install**: Run `python main.py` and type 'yes' to activate the setup assistant.
2b **Manual Install**: 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. Install [SoX](https://sourceforge.net/projects/sox/files/sox/)
4. Run `pip3 install -r requirements.txt`
4. Run `pip install -r requirements.txt`
5. Run `playwright install` and `playwright install-deps`.
5. Run `playwright install` and `playwright install-deps`. (if this fails try adding python -m to the front of the command)
6. Run `python3 main.py` (unless you chose automatic install, then the installer will automatically run main.py)
6. Run `python main.py` (unless you chose automatic install, then the installer will automatically run main.py)
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.
7. Enjoy 😎
(Note if you got an error installing or running the bot try first rerunning the command with a three after the name e.g. python3 or pip3)
## Video
https://user-images.githubusercontent.com/66544866/173453972-6526e4e6-c6ef-41c5-ab40-5d275e724e7c.mp4

@ -59,9 +59,7 @@ class POLLY:
f.write(voice_data.content)
except (KeyError, JSONDecodeError):
if response.json()["error"] == "Text length is too long!":
chunks = [
m.group().strip() for m in re.finditer(r" *((.{0,499})(\.|.$))", req_text)
]
chunks = [m.group().strip() for m in re.finditer(r" *((.{0,499})(\.|.$))", req_text)]
audio_clips = []
cbn = sox.Combiner()

@ -67,7 +67,9 @@ noneng = [
class TikTok: # TikTok Text-to-Speech Wrapper
def __init__(self):
self.URI_BASE = "https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker="
self.URI_BASE = (
"https://api16-normal-useast5.us.tiktokv.com/media/api/text/speech/invoke/?text_speaker="
)
def tts(
self,

@ -5,17 +5,20 @@ from dotenv import load_dotenv
from TTS.GTTS import GTTS
from TTS.POLLY import POLLY
from TTS.TikTok import TikTok
from utils.console import print_substep
CHOICE_DIR = {"tiktok": TikTok, "gtts": GTTS, 'polly': POLLY}
CHOICE_DIR = {"tiktok": TikTok, "gtts": GTTS, "polly": POLLY}
class TTS:
def __new__(cls):
load_dotenv()
try:
CHOICE = getenv("TTsChoice").casefold()
except AttributeError:
print_substep("None defined. Defaulting to 'polly.'")
CHOICE = "polly"
valid_keys = [key.lower() for key in CHOICE_DIR.keys()]
if CHOICE not in valid_keys:
raise ValueError(
f"{CHOICE} is not valid. Please use one of these {valid_keys} options"
)
raise ValueError(f"{CHOICE} is not valid. Please use one of these {valid_keys} options")
return CHOICE_DIR.get(CHOICE)()

@ -6,11 +6,12 @@ from os import getenv, name
from reddit.subreddit import get_subreddit_threads
from utils.cleanup import cleanup
from utils.console import print_markdown, print_step
# from utils.checker import envUpdate
from video_creation.background import download_background, chop_background_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
VERSION = 2.1
print(
"""
@ -37,6 +38,7 @@ reddit2fa = getenv("REDDIT_2FA")
def main():
#envUpdate()
cleanup()
def get_obj():

@ -1,3 +1,4 @@
import re
from os import getenv, environ
import praw
@ -14,6 +15,13 @@ def textify(text):
return "".join(filter(TEXT_WHITELIST.__contains__, text))
def try_env(param, backup):
try:
return environ[param]
except KeyError:
return backup
def get_subreddit_threads():
"""
Returns a list of threads from the AskReddit subreddit.
@ -44,10 +52,15 @@ def get_subreddit_threads():
print_step("Getting subreddit threads...")
if not getenv(
"SUBREDDIT"
): # note to self. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython")
): # note to user. you can have multiple subreddits via reddit.subreddit("redditdev+learnpython")
try:
subreddit = reddit.subreddit(
input("What subreddit would you like to pull from? ")
) # if the env isnt set, ask user
re.sub(r"r\/", "", input("What subreddit would you like to pull from? "))
# removes the r/ from the input
)
except ValueError:
subreddit = reddit.subreddit("askreddit")
print_substep("Subreddit not defined. Using AskReddit.")
else:
print_substep(f"Using subreddit: r/{getenv('SUBREDDIT')} from environment variable config")
subreddit = reddit.subreddit(
@ -83,7 +96,7 @@ def get_subreddit_threads():
if top_level_comment.body in ["[removed]", "[deleted]"]:
continue # # see https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/78
if not top_level_comment.stickied:
if len(top_level_comment.body) <= int(environ["MAX_COMMENT_LENGTH"]):
if len(top_level_comment.body) <= int(try_env("MAX_COMMENT_LENGTH", 500)):
content["comments"].append(
{
"comment_body": top_level_comment.body,

@ -40,14 +40,10 @@ def handle_input(
except ValueError:
console.log("[red]" + err_message) # Type conversion failed
continue
if (
nmin is not None and len(user_input) < nmin
): # Check if string is long enough
if nmin is not None and len(user_input) < nmin: # Check if string is long enough
console.log("[red]" + oob_error)
continue
if (
nmax is not None and len(user_input) > nmax
): # Check if string is not too long
if nmax is not None and len(user_input) > nmax: # Check if string is not too long
console.log("[red]" + oob_error)
continue
break

@ -5,9 +5,7 @@ from os.path import exists
def cleanup() -> int:
if exists("./assets/temp"):
count = 0
files = [
f for f in os.listdir(".") if f.endswith(".mp4") and "temp" in f.lower()
]
files = [f for f in os.listdir(".") if f.endswith(".mp4") and "temp" in f.lower()]
count += len(files)
for f in files:
os.remove(f)

@ -0,0 +1,9 @@
$envFile = Get-Content ".\.env.template"
$envFile -split "=" | Where-Object {$_ -notmatch '\"'} | Set-Content ".\envVarsbefSpl.txt"
Get-Content ".\envVarsbefSpl.txt" | Where-Object {$_ -notmatch '\#'} | Set-Content ".\envVarsN.txt"
Get-Content ".\envVarsN.txt" | Where-Object {$_ -ne ''} | Set-Content ".\video_creation\data\envvars.txt"
Remove-Item ".\envVarsbefSpl.txt"
Remove-Item ".\envVarsN.txt"
Write-Host $nowSplit

@ -0,0 +1,9 @@
$envFile = Get-Content ".\.env"
$envFile -split "=" | Where-Object {$_ -notmatch '\"'} | Set-Content ".\envVarsbefSpl.txt"
Get-Content ".\envVarsbefSpl.txt" | Where-Object {$_ -notmatch '\#'} | Set-Content ".\envVarsN.txt"
Get-Content ".\envVarsN.txt" | Where-Object {$_ -ne ''} | Set-Content ".\video_creation\data\envvars.txt"
Remove-Item ".\envVarsbefSpl.txt"
Remove-Item ".\envVarsN.txt"
Write-Host $nowSplit

@ -14,11 +14,14 @@ def get_subreddit_undone(submissions: List, subreddit):
if already_done(done_videos, submission):
continue
if submission.over_18:
try:
if getenv("ALLOW_NSFW").casefold() == "false":
print_substep("NSFW Post Detected. Skipping...")
continue
except AttributeError:
print_substep("NSFW settings not defined. Skipping NSFW post...")
return submission
print('all submissions have been done going by top submission order')
print("all submissions have been done going by top submission order")
return get_subreddit_undone(
subreddit.top(time_filter="hour"), subreddit
) # all of the videos in hot have already been done

@ -40,9 +40,7 @@ def download_background():
"assets/backgrounds", filename=f"{credit}-{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):

@ -1,2 +0,0 @@
videos.json
#todo add videos on github

@ -61,8 +61,7 @@ def make_final_video(number_of_clips, length):
ImageClip("assets/temp/png/title.png")
.set_duration(audio_clips[0].duration)
.set_position("center")
.resize(width=W - 100)
.set_opacity(float(opacity)),
.resize(width=W - 100),
)
else:
image_clips.insert(
@ -70,7 +69,8 @@ def make_final_video(number_of_clips, length):
ImageClip("assets/temp/png/title.png")
.set_duration(audio_clips[0].duration)
.set_position("center")
.resize(width=W - 100),
.resize(width=W - 100)
.set_opacity(float(opacity)),
)
for i in range(0, number_of_clips):

@ -40,7 +40,7 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num):
context.add_cookies(cookies) # load preference cookies
# Get the thread screenshot
page = context.new_page()
page.goto(reddit_object["thread_url"])
page.goto(reddit_object["thread_url"], timeout=0)
page.set_viewport_size(ViewportSize(width=1920, height=1080))
if page.locator('[data-testid="content-gate"]').is_visible():
# This means the post is NSFW and requires to click the proceed button.
@ -51,9 +51,7 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num):
'[data-click-id="text"] button'
).click() # Remove "Click to see nsfw" Button in Screenshot
page.locator('[data-test-id="post-content"]').screenshot(
path="assets/temp/png/title.png"
)
page.locator('[data-test-id="post-content"]').screenshot(path="assets/temp/png/title.png")
if storymode:
page.locator('[data-click-id="text"]').screenshot(
path="assets/temp/png/story_content.png"
@ -70,7 +68,7 @@ def download_screenshots_of_reddit_posts(reddit_object, screenshot_num):
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"]}')
page.goto(f'https://reddit.com{comment["comment_url"]}', timeout=0)
page.locator(f"#t1_{comment['comment_id']}").screenshot(
path=f"assets/temp/png/comment_{idx}.png"
)

Loading…
Cancel
Save