commit
5e98716768
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
@ -0,0 +1,13 @@
|
||||
from gtts import gTTS
|
||||
|
||||
|
||||
class GTTS:
|
||||
def tts(
|
||||
self,
|
||||
req_text: str = "Google Text To Speech",
|
||||
filename: str = "title.mp3",
|
||||
random_speaker=False,
|
||||
censor=False,
|
||||
):
|
||||
tts = gTTS(text=req_text, lang="en", slow=False)
|
||||
tts.save(f"{filename}")
|
@ -0,0 +1,106 @@
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
import requests
|
||||
import sox
|
||||
from moviepy.audio.AudioClip import concatenate_audioclips, CompositeAudioClip
|
||||
from moviepy.audio.io.AudioFileClip import AudioFileClip
|
||||
from requests.exceptions import JSONDecodeError
|
||||
|
||||
voices = [
|
||||
"Brian",
|
||||
"Emma",
|
||||
"Russell",
|
||||
"Joey",
|
||||
"Matthew",
|
||||
"Joanna",
|
||||
"Kimberly",
|
||||
"Amy",
|
||||
"Geraint",
|
||||
"Nicole",
|
||||
"Justin",
|
||||
"Ivy",
|
||||
"Kendra",
|
||||
"Salli",
|
||||
"Raveena",
|
||||
]
|
||||
|
||||
|
||||
# valid voices https://lazypy.ro/tts/
|
||||
|
||||
|
||||
class POLLY:
|
||||
def __init__(self):
|
||||
self.url = "https://streamlabs.com/polly/speak"
|
||||
|
||||
def tts(
|
||||
self,
|
||||
req_text: str = "Amazon Text To Speech",
|
||||
filename: str = "title.mp3",
|
||||
random_speaker=False,
|
||||
censor=False,
|
||||
):
|
||||
if random_speaker:
|
||||
voice = self.randomvoice()
|
||||
else:
|
||||
if not os.getenv("VOICE"):
|
||||
return ValueError(
|
||||
"Please set the environment variable VOICE to a valid voice. options are: {}".format(
|
||||
voices
|
||||
)
|
||||
)
|
||||
voice = str(os.getenv("VOICE")).capitalize()
|
||||
body = {"voice": voice, "text": req_text, "service": "polly"}
|
||||
response = requests.post(self.url, data=body)
|
||||
try:
|
||||
voice_data = requests.get(response.json()["speak_url"])
|
||||
with open(filename, "wb") as f:
|
||||
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)]
|
||||
|
||||
audio_clips = []
|
||||
cbn = sox.Combiner()
|
||||
|
||||
chunkId = 0
|
||||
for chunk in chunks:
|
||||
body = {"voice": voice, "text": chunk, "service": "polly"}
|
||||
resp = requests.post(self.url, data=body)
|
||||
voice_data = requests.get(resp.json()["speak_url"])
|
||||
with open(filename.replace(".mp3", f"-{chunkId}.mp3"), "wb") as out:
|
||||
out.write(voice_data.content)
|
||||
|
||||
audio_clips.append(filename.replace(".mp3", f"-{chunkId}.mp3"))
|
||||
|
||||
chunkId = chunkId + 1
|
||||
try:
|
||||
if len(audio_clips) > 1:
|
||||
cbn.convert(samplerate=44100, n_channels=2)
|
||||
cbn.build(audio_clips, filename, "concatenate")
|
||||
else:
|
||||
os.rename(audio_clips[0], filename)
|
||||
except (
|
||||
sox.core.SoxError,
|
||||
FileNotFoundError,
|
||||
): # https://github.com/JasonLovesDoggo/RedditVideoMakerBot/issues/67#issuecomment-1150466339
|
||||
for clip in audio_clips:
|
||||
i = audio_clips.index(clip) # get the index of the clip
|
||||
audio_clips = (
|
||||
audio_clips[:i] + [AudioFileClip(clip)] + audio_clips[i + 1 :]
|
||||
) # replace the clip with an AudioFileClip
|
||||
audio_concat = concatenate_audioclips(audio_clips)
|
||||
audio_composite = CompositeAudioClip([audio_concat])
|
||||
audio_composite.write_audiofile(filename, 44100, 2, 2000, None)
|
||||
|
||||
def make_readable(self, text):
|
||||
"""
|
||||
Amazon Polly fails to read some symbols properly such as '& (and)'.
|
||||
So we normalize input text before passing it to the service
|
||||
"""
|
||||
text = text.replace("&", "and")
|
||||
return text
|
||||
|
||||
def randomvoice(self):
|
||||
return random.choice(voices)
|
@ -0,0 +1,24 @@
|
||||
from os import getenv
|
||||
|
||||
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}
|
||||
|
||||
|
||||
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")
|
||||
return CHOICE_DIR.get(CHOICE)()
|
@ -0,0 +1,67 @@
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from os import path
|
||||
from sys import platform
|
||||
|
||||
ACCEPTABLE_TO_BE_LEFT_BLANK = ["RANDOM_THREAD", "TIMES_TO_RUN"]
|
||||
|
||||
|
||||
def envUpdate():
|
||||
if path.exists(".env.template"): # if .env.template exists and .env does not exist
|
||||
if platform == "win32" or platform == "cygwin":
|
||||
runPS("utils/scripts/FileGrabber.ps1")
|
||||
with open(".\\video_creation\\data\\envvars.txt", "rb") as f:
|
||||
envTemplate = f.read()
|
||||
elif platform == "darwin" or platform == "linux":
|
||||
envTemplate = subprocess.check_output(
|
||||
"awk -F '=' 'NF {print $1}' .env.template | grep --regexp=^[a-zA-Z]",
|
||||
shell=True,
|
||||
)
|
||||
else:
|
||||
raise OSError("Unsupported platform")
|
||||
elif path.exists(".env"):
|
||||
if platform == "win32" or platform == "cygwin":
|
||||
runPS("utils/scripts/FileGrabberenv.ps1")
|
||||
with open(".\\video_creation\\data\\envvars.txt", "rb") as f:
|
||||
envTemplate = f.read()
|
||||
elif platform == "darwin" or platform == "linux":
|
||||
envTemplate = subprocess.check_output(
|
||||
"awk -F '=' 'NF {print $1}' .env | grep --regexp=^[a-zA-Z]",
|
||||
shell=True,
|
||||
)
|
||||
else:
|
||||
raise OSError("Unsupported platform")
|
||||
else:
|
||||
raise FileNotFoundError("No .env or .env.template file found")
|
||||
tempEnv = tempfile.TemporaryFile()
|
||||
tempEnv.write(envTemplate)
|
||||
tempEnv.seek(0)
|
||||
envVars = tempEnv.readlines()
|
||||
|
||||
missing = []
|
||||
isMissingEnvs = False
|
||||
for env in envVars:
|
||||
try:
|
||||
env = env.decode("utf-8").strip()
|
||||
except AttributeError:
|
||||
env = env.strip()
|
||||
|
||||
if env not in os.environ:
|
||||
if str(env) in ACCEPTABLE_TO_BE_LEFT_BLANK:
|
||||
continue
|
||||
isMissingEnvs = True
|
||||
missing.append(env)
|
||||
|
||||
if isMissingEnvs:
|
||||
printstr = ""
|
||||
[printstr + str(var) for var in missing]
|
||||
print(
|
||||
f"The following environment variables are missing: {printstr}. Please add them to the .env file."
|
||||
)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def runPS(cmd):
|
||||
completed = subprocess.run(["powershell", "-Command", cmd], capture_output=True)
|
||||
return completed
|
@ -0,0 +1,40 @@
|
||||
# write a class that takes .env file and parses it into a dictionary
|
||||
|
||||
from dotenv import dotenv_values
|
||||
|
||||
DEFAULTS = {
|
||||
"SUBREDDIT": "AskReddit",
|
||||
"ALLOW_NSFW": "False",
|
||||
"POST_ID": "",
|
||||
"THEME": "DARK",
|
||||
"REDDIT_2FA": "no",
|
||||
"TIMES_TO_RUN": "",
|
||||
"MAX_COMMENT_LENGTH": "500",
|
||||
"OPACITY": "1",
|
||||
"VOICE": "en_us_001",
|
||||
"STORYMODE": "False",
|
||||
}
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self):
|
||||
self.raw = dotenv_values("../.env")
|
||||
self.load_attrs()
|
||||
|
||||
def __getattr__(self, attr): # code completion for attributes fix.
|
||||
return getattr(self, attr)
|
||||
|
||||
def load_attrs(self):
|
||||
for key, value in self.raw.items():
|
||||
self.add_attr(key, value)
|
||||
|
||||
def add_attr(self, key, value):
|
||||
if value is None or value == "":
|
||||
setattr(self, key, DEFAULTS[key])
|
||||
else:
|
||||
setattr(self, key, str(value))
|
||||
|
||||
|
||||
config = Config()
|
||||
|
||||
print(config.SUBREDDIT)
|
@ -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
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"name": "USER",
|
||||
"value": "eyJwcmVmcyI6eyJ0b3BDb250ZW50RGlzbWlzc2FsVGltZSI6MCwiZ2xvYmFsVGhlbWUiOiJSRURESVQiLCJuaWdodG1vZGUiOnRydWUsImNvbGxhcHNlZFRyYXlTZWN0aW9ucyI6eyJmYXZvcml0ZXMiOmZhbHNlLCJtdWx0aXMiOmZhbHNlLCJtb2RlcmF0aW5nIjpmYWxzZSwic3Vic2NyaXB0aW9ucyI6ZmFsc2UsInByb2ZpbGVzIjpmYWxzZX0sInRvcENvbnRlbnRUaW1lc0Rpc21pc3NlZCI6MH19",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
"name": "USER",
|
||||
"value": "eyJwcmVmcyI6eyJ0b3BDb250ZW50RGlzbWlzc2FsVGltZSI6MCwiZ2xvYmFsVGhlbWUiOiJSRURESVQiLCJuaWdodG1vZGUiOnRydWUsImNvbGxhcHNlZFRyYXlTZWN0aW9ucyI6eyJmYXZvcml0ZXMiOmZhbHNlLCJtdWx0aXMiOmZhbHNlLCJtb2RlcmF0aW5nIjpmYWxzZSwic3Vic2NyaXB0aW9ucyI6ZmFsc2UsInByb2ZpbGVzIjpmYWxzZX0sInRvcENvbnRlbnRUaW1lc0Rpc21pc3NlZCI6MH19",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
}
|
||||
]
|
||||
|
@ -1,2 +0,0 @@
|
||||
videos.json
|
||||
#todo add videos on github
|
@ -1,14 +1,14 @@
|
||||
[
|
||||
{
|
||||
"name": "USER",
|
||||
"value": "eyJwcmVmcyI6eyJ0b3BDb250ZW50RGlzbWlzc2FsVGltZSI6MCwiZ2xvYmFsVGhlbWUiOiJSRURESVQiLCJuaWdodG1vZGUiOnRydWUsImNvbGxhcHNlZFRyYXlTZWN0aW9ucyI6eyJmYXZvcml0ZXMiOmZhbHNlLCJtdWx0aXMiOmZhbHNlLCJtb2RlcmF0aW5nIjpmYWxzZSwic3Vic2NyaXB0aW9ucyI6ZmFsc2UsInByb2ZpbGVzIjpmYWxzZX0sInRvcENvbnRlbnRUaW1lc0Rpc21pc3NlZCI6MH19",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
"name": "USER",
|
||||
"value": "eyJwcmVmcyI6eyJ0b3BDb250ZW50RGlzbWlzc2FsVGltZSI6MCwiZ2xvYmFsVGhlbWUiOiJSRURESVQiLCJuaWdodG1vZGUiOnRydWUsImNvbGxhcHNlZFRyYXlTZWN0aW9ucyI6eyJmYXZvcml0ZXMiOmZhbHNlLCJtdWx0aXMiOmZhbHNlLCJtb2RlcmF0aW5nIjpmYWxzZSwic3Vic2NyaXB0aW9ucyI6ZmFsc2UsInByb2ZpbGVzIjpmYWxzZX0sInRvcENvbnRlbnRUaW1lc0Rpc21pc3NlZCI6MH19",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
},
|
||||
{
|
||||
"name": "eu_cookie",
|
||||
"value": "{%22opted%22:true%2C%22nonessential%22:false}",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
"name": "eu_cookie",
|
||||
"value": "{%22opted%22:true%2C%22nonessential%22:false}",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
}
|
||||
]
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"name": "eu_cookie",
|
||||
"value": "{%22opted%22:true%2C%22nonessential%22:false}",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
"name": "eu_cookie",
|
||||
"value": "{%22opted%22:true%2C%22nonessential%22:false}",
|
||||
"domain": ".reddit.com",
|
||||
"path": "/"
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in new issue