Skip to content

Instantly share code, notes, and snippets.

@jeffehobbs
Last active September 9, 2023 13:00
Show Gist options
  • Save jeffehobbs/23117c0cc1e167c9da6cd1ef315f65f2 to your computer and use it in GitHub Desktop.
Save jeffehobbs/23117c0cc1e167c9da6cd1ef315f65f2 to your computer and use it in GitHub Desktop.
ariseCthulhuBot - using ML to summon The Old Ones
# ariseCthulhuBot.py | jeffehobbs@gmail.com
# taking the Great Tweets and sending them through the
# Lovecraftian-chaotic wheat thresher of the "bark" TTS library
# in order to summon The Old Ones
#
# to-do:
#
# 1. [X] get a CLASSIC tweet
# 2. [X] make a music intro
# 3. [X] make a text tweet
# 4. [X] laugh track/sfx track
# 5. [X] make a music outro
# 6. [X] concatinate the audio files
# 7. [X] make a ffpeg waveform video
# 7a.[X] do it without destroying the host machine
# 8. [X] post it on twitter
from bark import SAMPLE_RATE, generate_audio, preload_models
from scipy.io.wavfile import write as write_wav
from pydub import AudioSegment
import os, json, random, glob, subprocess, time, configparser, tweepy
# globals
SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
INPUT_FILE = SCRIPT_PATH + '/input/dril_tweets.txt'
FORBIDDEN = ['@','http','bitch']
CREATE_NEW_POST = True
# not really sure this does anything but hey
os.environ["SUNO_OFFLOAD_CPU"] = "True"
os.environ["SUNO_USE_SMALL_MODELS"] = "True"
# set up API keys from external config secrets.txt file
config = configparser.ConfigParser()
config.read(SCRIPT_PATH +'/secrets.txt')
TWITTER_CONSUMER_KEY = config.get('twitter', 'consumer_key')
TWITTER_CONSUMER_SECRET = config.get('twitter', 'consumer_secret')
TWITTER_ACCESS_TOKEN = config.get('twitter', 'access_token')
TWITTER_ACCESS_TOKEN_SECRET = config.get('twitter', 'access_token_secret')
def get_dril_tweet(INPUT_FILE):
print("...getting line from file...")
lines = open(INPUT_FILE).read().splitlines()
random_line = random.choice(lines)
for forbade in FORBIDDEN:
if forbade in str(random_line):
get_dril_tweet(INPUT_FILE)
random_line = '] ' + random_line.replace('. ','.\n] ')
return(random_line)
def make_music(filename):
text_prompt = '[music]'
audio_array = generate_audio(text_prompt)
write_wav(f"{SCRIPT_PATH}/temp/{filename}.wav", SAMPLE_RATE, audio_array)
def make_text(text_prompt):
mode_type = random.randint(0, 1)
if (mode_type == 0):
mode = "♪"
else:
mode = ""
audio_array = generate_audio(mode + ' ' + text_prompt + ' ' + mode)
write_wav(f"{SCRIPT_PATH}/temp/text.wav", SAMPLE_RATE, audio_array)
def make_sfx_track():
mode_type = random.randint(0, 4)
if (mode_type == 0):
text_prompt = '[laughs]'
elif (mode_type == 1):
text_prompt = '[laughter]'
elif (mode_type == 2):
text_prompt = '[sighs]'
elif (mode_type == 3):
text_prompt = '[gasps]'
else:
text_prompt = '[clears throat]'
audio_array = generate_audio(text_prompt)
write_wav(f"{SCRIPT_PATH}/temp/laughs.wav", SAMPLE_RATE, audio_array)
def concatinate_audio():
intro = AudioSegment.from_wav(f"{SCRIPT_PATH}/temp/intro.wav")
text = AudioSegment.from_wav(f"{SCRIPT_PATH}/temp/text.wav")
laughs = AudioSegment.from_wav(f"{SCRIPT_PATH}/temp/laughs.wav")
outro = AudioSegment.from_wav(f"{SCRIPT_PATH}/temp/outro.wav")
full_presentation = intro + text + laughs + outro
full_presentation.export(f"{SCRIPT_PATH}/temp/audio.mp3", format="mp3")
def make_waveform_video(filename, text):
mode_type = random.randint(0, 1)
if (mode_type == 0):
mode = "line"
else:
mode = "cline"
colors = "green"
ffmpeg_command = "ffmpeg -hide_banner -loglevel warning -y -i " + filename + " -filter_complex '[0:a]showwaves=size=1280x720:mode=" + mode + ":colors=" + colors + ",format=yuv420p[v]' -map '[v]' -map 0:a -c:v libx264 -c:a copy " + filename.replace(".mp3","_waveform.mp4")
print(ffmpeg_command)
subprocess.call(ffmpeg_command, shell=True)
ffmpeg_command = "ffmpeg -hide_banner -loglevel warning -y -i " + filename.replace(".mp3","_waveform.mp4") + " -i " + filename + " -c:v copy -c:a aac " + SCRIPT_PATH + "/temp/video.mp4"
print(ffmpeg_command)
subprocess.call(ffmpeg_command, shell=True)
text = text.replace('"','').replace("'","").replace(":","")
ffmpeg_command = "ffmpeg -hide_banner -loglevel warning -y -i " + SCRIPT_PATH + "/temp/video.mp4 -vf \"drawtext=text='" + text + "':fontfile=" + SCRIPT_PATH + "/fonts/PrintChar21.ttf:fontsize=18:fontcolor=green:x=10:y=10\" -codec:a copy " + SCRIPT_PATH + "/output/video.mp4"
print(ffmpeg_command)
subprocess.call(ffmpeg_command, shell=True)
def clean_up():
files = glob.glob(f'{SCRIPT_PATH}/temp/*')
for f in files:
os.remove(f)
# tweet that stuff
def send_tweet(status, media_file_path):
media_ids = []
client = tweepy.Client(consumer_key=TWITTER_CONSUMER_KEY,
consumer_secret=TWITTER_CONSUMER_SECRET,
access_token=TWITTER_ACCESS_TOKEN,
access_token_secret=TWITTER_ACCESS_TOKEN_SECRET)
auth = tweepy.OAuth1UserHandler(
TWITTER_CONSUMER_KEY,
TWITTER_CONSUMER_SECRET,
TWITTER_ACCESS_TOKEN,
TWITTER_ACCESS_TOKEN_SECRET,
)
api = tweepy.API(auth)
media_upload_response = api.media_upload(media_file_path)
media_ids.append(media_upload_response.media_id)
response = client.create_tweet(text=status, user_auth=True, media_ids=media_ids)
return
def send_mastodon(status, media_file_path):
mastodon = Mastodon(
access_token = MASTODON_ACCESS_TOKEN,
api_base_url = 'https://botsin.space/'
)
media = mastodon.media_post(media_file_path, description="Very real photo of the incident")
mastodon.status_post(status, media_ids=media)
return
def main():
start = time.time()
if (CREATE_NEW_POST):
preload_models(
text_use_small=True,
coarse_use_small=True,
fine_use_gpu=True,
fine_use_small=True
)
text = get_dril_tweet(INPUT_FILE)
print(text)
make_music('intro')
make_text(text)
make_sfx_track()
make_music('outro')
concatinate_audio()
make_waveform_video(f'{SCRIPT_PATH}/temp/audio.mp3', text)
send_tweet('', f'{SCRIPT_PATH}/output/video.mp4')
clean_up()
end = time.time()
execution_time = int(end - start)
print('TIME:' + str(round(execution_time / 60)) + ' minutes')
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment