refactor(video.py): remove unnecessary print statement

refactor(montage.py): use os.path.join instead of string concatenation
refactor(thumbnail.py): use os.path.join instead of string concatenation
refactor(uploader.py): use os.path.join instead of string concatenation
refactor(uploader.py): add support for client_secret.json file with different names in credentials folder
docs(readme.md): update instructions for openai and unsplash keys
docs(readme.md): update instructions for google oauth client id json files
docs(readme.md): add information about channel.yaml file in each channel's folder
This commit is contained in:
Paillat
2023-06-25 17:40:01 +02:00
parent 268be54efe
commit 131e64eae0
5 changed files with 34 additions and 28 deletions

View File

@@ -31,8 +31,7 @@ class Video:
os.makedirs( self.path) os.makedirs( self.path)
script = None script = None
if os.path.exists(os.path.join( self.path, "script.json")): if os.path.exists(os.path.join( self.path, "script.json")):
printm("Video script already exists. Do you want to overwrite it ?") if input("Video script already exists. Do you want to overwrite it ? (y/N) : ") == "y":
if input("y/N") == "y":
os.remove(os.path.join( self.path, "script.json")) os.remove(os.path.join( self.path, "script.json"))
if not os.path.exists(os.path.join( self.path, "script.json")): if not os.path.exists(os.path.join( self.path, "script.json")):
@@ -51,7 +50,7 @@ class Video:
"description": self.idea['description'] + "\n\n" + credits, "description": self.idea['description'] + "\n\n" + credits,
} }
await generate_thumbnail( self.path, self.idea['title'], self.idea['description']) await generate_thumbnail( self.path, self.idea['title'], self.idea['description'])
videoid = await upload_video( self.path, self.idea['title'], self.metadata['description'], 28, "", "private", self.path) videoid = await upload_video( self.path, self.idea['title'], self.metadata['description'], 28, "", "private", self.parent.path)
printm(f"Your video is ready! You can find it in { self.path}") printm(f"Your video is ready! You can find it in { self.path}")
video_meta_file = { video_meta_file = {
"title": self.idea['title'], "title": self.idea['title'],

View File

@@ -18,7 +18,7 @@ if not unsplash_access:
unsplash_url = "https://api.unsplash.com/photos/random/?client_id=" + unsplash_access + "&query=" unsplash_url = "https://api.unsplash.com/photos/random/?client_id=" + unsplash_access + "&query="
async def prepare(path): async def prepare(path):
with open(path + "/script.json", 'r', encoding='utf-8') as f: with open(os.path.join(path, "script.json"), 'r', encoding='utf-8') as f:
script = json.load(f) script = json.load(f)
f.close() f.close()
if not os.path.exists(path + "/slides"): os.mkdir(path + "/slides") if not os.path.exists(path + "/slides"): os.mkdir(path + "/slides")
@@ -70,7 +70,7 @@ async def prepare(path):
with open(path + "/slides/slide" + str(i) + ".md", 'w', encoding='utf-8') as f: with open(path + "/slides/slide" + str(i) + ".md", 'w', encoding='utf-8') as f:
f.write(marp + "\n\n") # blank slide f.write(marp + "\n\n") # blank slide
for i in range(len(script)): for i in range(len(script)):
marrkdown_path = "./" + path + "/slides/slide" + str(i) + ".md" marrkdown_path = os.path.join(path, f"slides/slide{i}.md")
if os.path.exists(f"./{path}/slides/slide{i}.png"): if os.path.exists(f"./{path}/slides/slide{i}.png"):
#skip this slide #skip this slide
continue continue
@@ -134,6 +134,6 @@ async def mount(path, script):
with open (randpath.split(".")[0] + ".txt", 'r', encoding='utf-8') as f: with open (randpath.split(".")[0] + ".txt", 'r', encoding='utf-8') as f:
music_credit = f.read() music_credit = f.read()
f.close() f.close()
return music_credit return music_credit or ""
else: else:
return None return None

View File

@@ -5,6 +5,7 @@ from PIL import Image
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from utils.openaicaller import openai from utils.openaicaller import openai
from utils.misc import open_explorer_here
''' '''
Putpose of this file is to generate a miniature of the video. Putpose of this file is to generate a miniature of the video.
It has a function that takes a path, title, and description and generates a miniature. It has a function that takes a path, title, and description and generates a miniature.
@@ -47,7 +48,7 @@ async def rand_gradient(image):
async def generate_thumbnail(path, title, description): async def generate_thumbnail(path, title, description):
prmpt = prompt.replace("[TITLE]", title).replace("[DESCRIPTION]", description) prmpt = prompt.replace("[TITLE]", title).replace("[DESCRIPTION]", description)
response = openai.generate_response( response = await openai.generate_response(
model="gpt-4", model="gpt-4",
messages=[ messages=[
{"role":"user","content":prmpt}, {"role":"user","content":prmpt},
@@ -58,14 +59,18 @@ async def generate_thumbnail(path, title, description):
await generate_image(path, text1, text2) await generate_image(path, text1, text2)
async def generate_image(path, text1, text2): async def generate_image(path, text1, text2):
path_to_bcg = path.split("/")[:-1] # path_to_bcg = path.split("/")[:-1]
path_to_bcg = "/".join(path_to_bcg) # path_to_bcg = "/".join(path_to_bcg)
#use os instead
path_to_bcg = os.path.dirname(os.path.dirname(path))
print(path_to_bcg) print(path_to_bcg)
if not os.path.exists(f"{path_to_bcg}/bcg.png"): if not os.path.exists(f"{path_to_bcg}/bcg.png"):
input("bcg.png not found. Please put bcg.png in the same folder as the video."+path_to_bcg) input("bcg.png not found. Please put bcg.png in the folder that will open. Press enter to open the folder.")
open_explorer_here(path_to_bcg)
input("Press enter when you have put bcg.png in the folder.")
if not os.path.exists(f"{path_to_bcg}/bcg.png"): if not os.path.exists(f"{path_to_bcg}/bcg.png"):
input("bcg.png still not found. Exiting.") input("bcg.png still not found. Exiting.")
exit() raise FileNotFoundError("bcg.png not found")
bcg = Image.open(f"{path_to_bcg}/bcg.png") bcg = Image.open(f"{path_to_bcg}/bcg.png")
img = Image.new('RGBA', (1920, 1080)) img = Image.new('RGBA', (1920, 1080))
img, textcolor1, textcolor2 = await rand_gradient(img) img, textcolor1, textcolor2 = await rand_gradient(img)

View File

@@ -10,21 +10,11 @@ pip install -r requirements.txt
``` ```
You will also need to install [FFmpeg](https://ffmpeg.org/download.html) in order for `moviepy` to work. You will also need to install [FFmpeg](https://ffmpeg.org/download.html) in order for `moviepy` to work.
Now that you have installed the dependencies, you will need to add all the required values to the `.env` file like this: You will need an `openai_api_key` and an `unsplash_access_key` in order to use the script. You can get them [here](https://beta.openai.com/), and [here](https://unsplash.com/developers). You will be told when to paste them. You can always change them later in the `env.yaml` file.
```
OPENAI_API_KEY=
UNSPLASH_ACCESS_KEY=
DEEPL_ACCESS_KEY=
```
You should be able to find all the required keys with a quick Google search.
Now, open the blank text file called `subjects.txt` in the `env` folder and add the subjects you want to generate videos for, one per line. For example: You should also prepare google oauth client id json for the youtube api for each account you want to automate. You can find a tutorial on how to do that [here](https://developers.google.com/youtube/v3/quickstart/python). You will be asked to add it to the corresponding folder when you run the script for the first time for each channel.
```
Educational and simple videos about physics
Python tutorials for both beginners and advanced programmers
```
One per line.
You should also prepare google oauth client id json for the youtube api for each account youo want to automate. You can find a tutorial on how to do that [here](https://developers.google.com/youtube/v3/quickstart/python). You will be asked to add it to the corresponding folder when you run the script for the first time for each subject.
Now, you can run the `main.py` script to generate the videos. If it is the first time you are running the script, it will ask you to log in to your Google account, as well as creating and moving or creating some files. The created videos will be saved in the `videos` folder, and automatically uploaded to YouTube. The script will also create a thumbnail and a description for each video, and add a nice background music to it. Now, you can run the `main.py` script to generate the videos. If it is the first time you are running the script, it will ask you to log in to your Google account, as well as creating and moving or creating some files. The created videos will be saved in the `videos` folder, and automatically uploaded to YouTube. The script will also create a thumbnail and a description for each video, and add a nice background music to it.
# Other stuff
In each channel's folder there will be a `channel.yaml` file with all of the channel's settings. You can change them manually, but you will need to restart the script for the changes to take effect.

View File

@@ -39,7 +39,19 @@ VALID_PRIVACY_STATUSES = ('public', 'private', 'unlisted')
# Authorize the request and store authorization credentials. # Authorize the request and store authorization credentials.
async def get_authenticated_service(credentialsPath=""): async def get_authenticated_service(credentialsPath=""):
CLIENT_SECRETS_FILE=f'{credentialsPath}/client_secret.json' CLIENT_SECRETS_FILE = ""
try:
CLIENT_SECRETS_FILE=os.path.join(credentialsPath, "client_secret.json")
except:
listdir = os.listdir(credentialsPath)
for file in listdir:
if file.startswith("client_secret"):
#rename file to client_secret.json
os.rename(os.path.join(credentialsPath, file), os.path.join(credentialsPath, "client_secret.json"))
CLIENT_SECRETS_FILE=os.path.join(credentialsPath, "client_secret.json")
break
if CLIENT_SECRETS_FILE == "":
raise FileNotFoundError("No client_secret.json file found in the specified path !")
if os.path.exists(f'{credentialsPath}/credentials.json'): if os.path.exists(f'{credentialsPath}/credentials.json'):
with open(f'{credentialsPath}/credentials.json') as json_file: with open(f'{credentialsPath}/credentials.json') as json_file:
data = json.load(json_file) data = json.load(json_file)
@@ -143,7 +155,7 @@ async def upload_video(path, title, description, category, keywords, privacyStat
print('An HTTP error %d occurred:\n%s' % (e.resp.status, e.content)) print('An HTTP error %d occurred:\n%s' % (e.resp.status, e.content))
async def upload_thumbnail(video_id, file, credentials_path=""): async def upload_thumbnail(video_id, file, credentials_path=""):
youtube = get_authenticated_service(credentials_path) youtube = await get_authenticated_service(credentials_path)
youtube.thumbnails().set( # type: ignore youtube.thumbnails().set( # type: ignore
videoId=video_id, videoId=video_id,
media_body=file media_body=file