feat: Bot Upgrade

- Video title, description & tags generation.
- Automating video upload to youtube
pull/2058/head
Mohamed Moataz 1 year ago
parent 50935a5d28
commit 98aaffd865

@ -50,9 +50,12 @@ def main(POST_ID=None) -> None:
global redditid, reddit_object global redditid, reddit_object
reddit_object = get_subreddit_threads(POST_ID) reddit_object = get_subreddit_threads(POST_ID)
redditid = id(reddit_object) redditid = id(reddit_object)
post_text = ' '.join(reddit_object['thread_post'])
length, number_of_comments = save_text_to_mp3(reddit_object) length, number_of_comments = save_text_to_mp3(reddit_object)
length = math.ceil(length) length = math.ceil(length)
# length, number_of_comments = 360, 43 # length, number_of_comments = 42, 5
get_screenshots_of_reddit_posts(reddit_object, number_of_comments) get_screenshots_of_reddit_posts(reddit_object, number_of_comments)
bg_config = { bg_config = {
"video": get_background_config("video"), "video": get_background_config("video"),
@ -61,7 +64,15 @@ def main(POST_ID=None) -> None:
download_background_video(bg_config["video"]) download_background_video(bg_config["video"])
download_background_audio(bg_config["audio"]) download_background_audio(bg_config["audio"])
chop_background(bg_config, length, reddit_object) chop_background(bg_config, length, reddit_object)
make_final_video(number_of_comments, length, reddit_object, bg_config) video_path = make_final_video(number_of_comments, length, reddit_object, bg_config)
video_data, thumbnail_text = get_video_data(post_text)
print("Video title:", video_data['title'])
print("Video description:", video_data['description'])
print("Video tags:", video_data['tags'])
# TODO: Generate thumbnail from text here
thumbnail = None
upload_video_to_youtube(video_path, video_data, thumbnail)
def run_many(times) -> None: def run_many(times) -> None:
@ -115,6 +126,9 @@ if __name__ == "__main__":
) )
config is False and sys.exit() config is False and sys.exit()
from video_data_generation.gemini import get_video_data
from utils.youtube_uploader import upload_video_to_youtube
if ( if (
not settings.config["settings"]["tts"]["tiktok_sessionid"] not settings.config["settings"]["tts"]["tiktok_sessionid"]
or settings.config["settings"]["tts"]["tiktok_sessionid"] == "" or settings.config["settings"]["tts"]["tiktok_sessionid"] == ""

@ -31,11 +31,11 @@ storymode_max_length = { optional = true, default = 1000, example = 1000, explan
resolution_w = { optional = false, default = 1080, example = 1440, explantation = "Sets the width in pixels of the final video" } resolution_w = { optional = false, default = 1080, example = 1440, explantation = "Sets the width in pixels of the final video" }
resolution_h = { optional = false, default = 1920, example = 2560, explantation = "Sets the height in pixels of the final video" } resolution_h = { optional = false, default = 1920, example = 2560, explantation = "Sets the height in pixels of the final video" }
zoom = { optional = true, default = 1, example = 1.1, explanation = "Sets the browser zoom level. Useful if you want the text larger.", type = "float", nmin = 0.1, nmax = 2, oob_error = "The text is really difficult to read at a zoom level higher than 2" } zoom = { optional = true, default = 1, example = 1.1, explanation = "Sets the browser zoom level. Useful if you want the text larger.", type = "float", nmin = 0.1, nmax = 2, oob_error = "The text is really difficult to read at a zoom level higher than 2" }
run_every = { optional = false, default = 24, example = 5, explanation = "How often should the bot create a video (in hours).", type = "int", nmin = 1, nmax = 48, oob_error = "Please choose a number between 1 and 24." } run_every = { optional = false, default = 24, example = 5, explanation = "How often should the bot create a video (in hours).", type = "int", nmin = 4, nmax = 48, oob_error = "Please choose a number between 4 and 48." }
[settings.background] [settings.background]
background_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["mudrunner", "granny-remake", ""], explanation = "Sets the background for the video based on game name" } background_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["mudrunner", "granny-remake", ""], explanation = "Sets the background for the video based on game name" }
background_audio = { optional = true, default = "lofi", example = "chill-summer", options = ["lofi","lofi-2","chill-summer", "eerie",""], explanation = "Sets the background audio for the video" } background_audio = { optional = true, default = "lofi", example = "chill-summer", options = ["eerie", "mysterious", "hybrid",""], explanation = "Sets the background audio for the video" }
background_audio_volume = { optional = true, type = "float", nmin = 0, nmax = 1, default = 0.15, example = 0.05, explanation="Sets the volume of the background audio. If you don't want background audio, set it to 0.", oob_error = "The volume HAS to be between 0 and 1", input_error = "The volume HAS to be a float number between 0 and 1"} background_audio_volume = { optional = true, type = "float", nmin = 0, nmax = 1, default = 0.15, example = 0.05, explanation="Sets the volume of the background audio. If you don't want background audio, set it to 0.", oob_error = "The volume HAS to be between 0 and 1", input_error = "The volume HAS to be a float number between 0 and 1"}
enable_extra_audio = { optional = true, type = "bool", default = false, example = false, explanation="Used if you want to render another video without background audio in a separate folder", input_error = "The value HAS to be true or false"} enable_extra_audio = { optional = true, type = "bool", default = false, example = false, explanation="Used if you want to render another video without background audio in a separate folder", input_error = "The value HAS to be true or false"}
background_thumbnail = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Generate a thumbnail for the video (put a thumbnail.png file in the assets/backgrounds directory.)" } background_thumbnail = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Generate a thumbnail for the video (put a thumbnail.png file in the assets/backgrounds directory.)" }
@ -60,3 +60,6 @@ python_voice = { optional = false, default = "1", example = "1", explanation = "
py_voice_num = { optional = false, default = "2", example = "2", explanation = "The number of system voices (2 are pre-installed in Windows)" } py_voice_num = { optional = false, default = "2", example = "2", explanation = "The number of system voices (2 are pre-installed in Windows)" }
silence_duration = { optional = true, example = "0.1", explanation = "Time in seconds between TTS comments", default = 0.3, type = "float" } silence_duration = { optional = true, example = "0.1", explanation = "Time in seconds between TTS comments", default = 0.3, type = "float" }
no_emojis = { optional = false, type = "bool", default = false, example = false, options = [true, false,], explanation = "Whether to remove emojis from the comments" } no_emojis = { optional = false, type = "bool", default = false, example = false, options = [true, false,], explanation = "Whether to remove emojis from the comments" }
[settings.gemini]
gemini_api_key = { optional = false, example = "21f13f91f54d741e2ae27d2ab1b99d59", explanation = "Gemini API key" }

@ -5,6 +5,11 @@
"eerie.mp3", "eerie.mp3",
"Royalty Free Music" "Royalty Free Music"
], ],
"mysterious":[
"https://www.youtube.com/watch?v=lRwq7pfA4Gg",
"mysterious.mp3",
"Royalty Free Music"
],
"hybrid":[ "hybrid":[
"https://www.youtube.com/watch?v=QbqkR5VNaU8", "https://www.youtube.com/watch?v=QbqkR5VNaU8",
"hybrid.mp3", "hybrid.mp3",

@ -57,16 +57,19 @@ def get_subreddit_undone(submissions: list, subreddit, times_checked=0, similari
else: else:
# Check for the length of the post text # Check for the length of the post text
if len(submission.selftext) > ( if len(submission.selftext) > (
settings.config["settings"]["storymode_max_length"] or 2000 settings.config["settings"]["storymode_max_length"]
): ):
print_substep( print_substep(
f"Post is too long ({len(submission.selftext)}), try with a different post. ({settings.config['settings']['storymode_max_length']} character limit)" f"Post is too long ({len(submission.selftext)}), try with a different post. ({settings.config['settings']['storymode_max_length']} character limit)"
) )
continue continue
elif len(submission.selftext) < 30: elif len(submission.selftext) < 400:
continue continue
if settings.config["settings"]["storymode"] and not submission.is_self: if settings.config["settings"]["storymode"] and not submission.is_self:
continue continue
if submission.upvote_ratio * 100 < 70 and not settings.config["reddit"]["thread"]["post_id"]:
print(f"Found a post with upvote ratio {submission.upvote_ratio*100}% which is less than 70%. Skipping...")
continue
if similarity_scores is not None: if similarity_scores is not None:
return submission, similarity_scores[i].item() return submission, similarity_scores[i].item()
return submission return submission

@ -0,0 +1,36 @@
from simple_youtube_api.Channel import Channel
from simple_youtube_api.LocalVideo import LocalVideo
from video_data_generation import gemini
# logging into the channel
channel = Channel()
channel.login("./utils/client_secret.json", "./utils/credentials.storage")
def upload_video_to_youtube(video_path, data, thumbnail):
# setting up the video that is going to be uploaded
video = LocalVideo(file_path=video_path)
# setting snippet
video.set_title(data['title'])
video.set_description(data["description"])
tags = data["tags"].split(', ')
if len(tags) == 1: tags = tags[0].split(',')
video.set_tags(tags)
# video.set_category("gaming")
video.set_default_language("en-US")
# setting status
video.set_embeddable(True)
video.set_license("youtube")
video.set_privacy_status("public")
video.set_public_stats_viewable(True)
# setting thumbnail
if thumbnail is not None: video.set_thumbnail_path(thumbnail)
# uploading video and printing the results
video = channel.upload_video(video)
print(video.id)
print(video)

@ -144,6 +144,16 @@ def make_final_video(
reddit_obj (dict): The reddit object that contains the posts to read. reddit_obj (dict): The reddit object that contains the posts to read.
background_config (Tuple[str, str, str, Any]): The background config to use. background_config (Tuple[str, str, str, Any]): The background config to use.
""" """
# title = re.sub(r"[^\w\s-]", "", reddit_obj["thread_title"])
# filename = f"{name_normalize(title)[:251]}"
# p = f'results/{settings.config["reddit"]["thread"]["subreddit"]}' + f"/{filename}"
# print((
# p[:251] + ".mp4"
# ))
# return (
# p[:251] + ".mp4"
# )
# settings values # settings values
W: Final[int] = int(settings.config["settings"]["resolution_w"]) W: Final[int] = int(settings.config["settings"]["resolution_w"])
H: Final[int] = int(settings.config["settings"]["resolution_h"]) H: Final[int] = int(settings.config["settings"]["resolution_h"])
@ -444,3 +454,4 @@ def make_final_video(
cleanups = cleanup(reddit_id) cleanups = cleanup(reddit_id)
print_substep(f"Removed {cleanups} temporary files 🗑") print_substep(f"Removed {cleanups} temporary files 🗑")
print_step("Done! 🎉 The video is in the results folder 📁") print_step("Done! 🎉 The video is in the results folder 📁")
return path

@ -0,0 +1,76 @@
import json
import google.generativeai as genai
from utils import settings
genai.configure(api_key=settings.config["settings"]["gemini"]["gemini_api_key"])
prompt1 = """I make some youtube videos where I get subreddits about ghost stories and make a video of it.
I will send a post to you and you should generate a YouTube video title, tags and description for it.
Only respond with the following:
title::: "The video title"
description::: "The video description"
tags::: "The video tags" without hashtags and separated by commas
Here is the post:
"""
prompt2 = """Describe a thumbnail for a youtube video about ghosts and ghost stories.
Only respond with the thumbnail description.
The description should be clear for an AI image generator model to generate it.
The video tags are:
"""
model = genai.GenerativeModel('gemini-pro')
def get_data(post):
response = model.generate_content(prompt1 + post)
text = response.text.split('\n')
data = {i.split(':::')[0].strip(): i.split(':::')[1].strip() for i in text}
return data
def get_thumbnail(post):
thumbnail_response = model.generate_content(prompt2+post)
return thumbnail_response.text
def get_video_data(post):
data = None
thumbnail = None
print("Generating video title & description...")
for i in range(2):
if i != 0: print("Try:", i+1)
try:
if data is None: data = get_data(post)
if data and thumbnail is None:
thumbnail = get_thumbnail(data['tags'])
break
except Exception as e:
# print(e)
continue
return data, thumbnail
if __name__ == '__main__':
post = """
This is my dads story. With all of the weirdest things that have happened, this one takes the cake.
My dad doesn't really speak of many paranormal experiences. This one freaked him out enough though, that he told me. One night, he went to bed with my mom and their dogs. My dad falls asleep pretty fast but for some reason that night, he couldn't.
Some background info: My parents at the time had two cats, Sweetpea and Baby Princess( my sister named her when she was 6) Their cats would sometimss go into their room when they were sleeping. My parents always slept with their door open(I could never, creeps me out). Their room is the first room when you would reach the top of the steps.
Anyways, my dad was laying in bed tossing and turning. He flipped on his back and closed his eyes. When he was laying there, he felt a pressure going up his legs. He figured the cats were walking up his legs. He then realized the pressure was going up his legs and ended up to his chest. He couldn't move a muscle. That's when he started to choke. His eyes popped open and he looked over and there was a figure standing over him with his hands around my dads throat.
According to my dad, the figure had a black WW2 Nazi uniform on with the black uniform hat that had a swastika on it. The choking lasted for some time until my dad told it to get the fuck off of him, then it disappeared. My dad was freaked out. He got up and checked inside all of our bedrooms and throughout the house but couldn't find anyone anywhere. Everyone was asleep.
I asked my dad why he thought the Nazi was choking him. He said he wasn't sure why, maybe he was an old angry relative. My grandma grew up in Germany through the war and my grandmas dad was a Nazi. So to say the least, we had some relatives that were in the war. I'm not sure why this ghost attacked my dad, maybe because he was the strongest in the house? Who knows.
My dad isn't one to make up stories like this, so I believe him. He's a very strong and brave man, and it takes a lot for him to get scared. He told me that this really got to him and that he was scared for us and this is why he got up and searched throughout the house. Usually, if paranormal things happen, he doesn't say much because he didn't want to scare us when we were kids.
I know that some of you might think that he had sleep paralysis when this happened, but my dad did not suffer from this. He told me that this was the first and only time that he couldn't move like this.
Thank you for reading! Let me know what you guys think :)
"""
for i in get_video_data(post):
print(i, end="\n\n")

@ -0,0 +1,66 @@
import requests
from bs4 import BeautifulSoup
s = requests.session()
def renew_connection():
csrf_url = "https://image.plus/"
payload = {}
csrf_headers = {
'Host': 'image.plus',
'Sec-Ch-Ua': '"Chromium";v="121", "Not A(Brand";v="99"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Priority': 'u=0, i',
'Connection': 'close'
}
s.cookies.clear()
response = s.request("GET", csrf_url, headers=csrf_headers)
print(response.status_code)
soup = BeautifulSoup(response.text, 'html.parser')
return soup.find("meta", {"name":"csrf-token"})['content']
def generate_image(csrf_token, prompt=None):
url = "https://image.plus/images/generate"
prompt = "A ghost flying with a person sitting scared on a couch."
payload = f"prompt={prompt.replace(' ', '+')}&negative_prompt=&model=1&style=cinematic&samples=1&size=1152x896"
headers = {
'Host': 'image.plus',
'Content-Length': '106',
'Sec-Ch-Ua': '"Chromium";v="121", "Not A(Brand";v="99"',
'Sec-Ch-Ua-Mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest',
'Sec-Ch-Ua-Platform': '"Windows"',
'Origin': 'https://image.plus',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://image.plus/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Priority': 'u=1, i',
'X-Csrf-Token': csrf_token,
}
response = s.request("POST", url, headers=headers, data=payload)
return response.json()
image = generate_image(renew_connection())
print(image)
Loading…
Cancel
Save