I mean its the biggest commit I ever did (did git add * cause I didn't want to explain all of this It's already so complicated)

This commit is contained in:
Paillat
2023-06-25 16:12:23 +02:00
parent 09c98a460b
commit e676d5851e
14 changed files with 568 additions and 123 deletions

35
utils/config.py Normal file
View File

@@ -0,0 +1,35 @@
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
loadingmessage = f"""{bcolors.OKGREEN}
▄████████ ▄████████ ▀█████████▄ ▄█ ▄████████
███ ███ ███ ███ ███ ███ ███ ███ ███
███ █▀ ███ ███ ███ ███ ███ ███ █▀
▄███▄▄▄ ███ ███ ▄███▄▄▄██▀ ███ ▄███▄▄▄
▀▀███▀▀▀ ▀███████████ ▀▀███▀▀▀██▄ ███ ▀▀███▀▀▀
███ ███ ███ ███ ██▄ ███ ███ █▄
███ ███ ███ ███ ███ ███▌ ▄ ███ ███
███ ███ █▀ ▄█████████▀ █████▄▄██ ██████████
{bcolors.ENDC}{bcolors.OKBLUE}Film and Artistic Bot for Lively Entertainment{bcolors.ENDC}
{bcolors.OKCYAN}
Made with 💖 by: *@paillat-dev*
https://paillat.dev
Thanks to *@Code7G* for the suggestions!
Thanks to *ChatGPT* for the name suggestions!
{bcolors.ENDC}
"""
if __name__ == '__main__':
print(loadingmessage)

89
utils/misc.py Normal file
View File

@@ -0,0 +1,89 @@
import os
import yaml
clear_screen = lambda: os.system('cls' if os.name == 'nt' else 'clear')
open_explorer_here = lambda path: os.system(f'explorer.exe "{path}"' if os.name == 'nt' else f'open "{path}"')
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class realbcolors:
PURPLE = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def printm(*args, **kwargs):
result = ''
underline_counter = 0
bold_counter = 0
sep = kwargs.get('sep', ' ')
text = sep.join([str(arg) for arg in args])
i = 0
while i < len(text):
if text[i:].startswith('$***'):
result += text[i:i+4].replace('$', '')
i += 4
continue
elif text[i:].startswith('$**'):
result += text[i:i+3].replace('$', '')
i += 3
continue
elif text[i:].startswith('$*'):
result += text[i:i+2].replace('$', '')
i += 2
continue
elif text[i:].startswith('***'):
if bold_counter % 2 == 0 and underline_counter % 2 == 0:
result += bcolors.BOLD + bcolors.UNDERLINE
elif bold_counter % 2 == 0:
result += bcolors.BOLD
elif underline_counter % 2 == 0:
result += bcolors.UNDERLINE
else:
result += bcolors.ENDC
i += 3
bold_counter += 1
underline_counter += 1
continue
elif text[i:].startswith('**'):
if bold_counter % 2 == 0:
result += bcolors.BOLD
else:
result += bcolors.ENDC
i += 2
bold_counter += 1
continue
elif text[i:].startswith('*'):
if underline_counter % 2 == 0:
result += bcolors.UNDERLINE
else:
result += bcolors.ENDC
i += 1
underline_counter += 1
continue
result += text[i]
i += 1
result += bcolors.ENDC # Ensure the formatting is reset at the end
print(text, **kwargs)
def getenv(var, default=None):
with open('env.yaml', 'r') as f:
env = yaml.safe_load(f)
return env.get(var, default)

8
utils/normalize_file.py Normal file
View File

@@ -0,0 +1,8 @@
import re
async def normalize_file(filename):
filename = re.sub(r'[<>:"|?*\s]', '_', filename)
#also shorten the filename if it's too long
if len(filename) > 30:
filename = filename[:27] + "___"
return filename

110
utils/openaicaller.py Normal file
View File

@@ -0,0 +1,110 @@
"""
This file provides a Python module that wraps the OpenAI API for making API calls.
The module includes:
- Functions for generating responses using chat-based models and handling API errors.
- Constants for chat and text models and their maximum token limits.
- Imports for required modules, including OpenAI and asyncio.
- A color formatting class, `bcolors`, for console output.
The main component is the `openai_caller` class with methods:
- `__init__(self, api_key=None)`: Initializes an instance of the class and sets the API key if provided.
- `set_api_key(self, key)`: Sets the API key for OpenAI.
- `generate_response(self, **kwargs)`: Asynchronously generates a response based on the provided arguments.
- `chat_generate(self, **kwargs)`: Asynchronously generates a chat-based response, handling token limits and API errors.
The module assumes the presence of `num_tokens_from_messages` function in a separate module called `utils.tokens`, used for token calculation.
Refer to function and method documentation for further details.
"""
import openai as openai_module
import asyncio
from openai.error import APIError, Timeout, RateLimitError, APIConnectionError, InvalidRequestError, AuthenticationError, ServiceUnavailableError
from utils.tokens import num_tokens_from_messages
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
chat_models = ["gpt-4", "gpt-4-32k", "gpt-3.5-turbo", "gpt-3.5-turbo-16k"]
text_models = ["text-davinci-003", "text-davinci-002", "text-curie-001", "text-babbage-001", "text-ada-001"]
models_max_tokens = {
"gpt-4": 8_192,
"gpt-4-32k": 32_768,
"gpt-3.5-turbo": 4_096,
"gpt-3.5-turbo-16k": 16_384,
"text-davinci-003": 4_097,
"text-davinci-002": 4_097,
"text-curie-001": 2_049,
"text-babbage-001": 2_049,
"text-ada-001": 2_049,
}
class openai_caller:
def __init__(self, api_key=None) -> None:
pass
def set_api_key(self, key):
openai_module.api_key = key
async def generate_response(self, **kwargs):
if kwargs['model'] in chat_models:
return await self.chat_generate(**kwargs)
elif kwargs['model'] in text_models:
raise NotImplementedError("Text models are not supported yet")
else:
raise ValueError("Model not found")
async def chat_generate(self, **kwargs):
tokens = await num_tokens_from_messages(kwargs['messages'], kwargs['model'])
model_max_tokens = models_max_tokens[kwargs['model']]
while tokens > model_max_tokens:
kwargs['messages'] = kwargs['messages'][1:]
print(f"{bcolors.BOLD}{bcolors.WARNING}Warning: Too many tokens. Removing first message.{bcolors.ENDC}")
tokens = await num_tokens_from_messages(kwargs['messages'], kwargs['model'])
i = 0
response = None
while i < 10:
try:
response = await openai_module.ChatCompletion.acreate(**kwargs)
break
except APIError:
await asyncio.sleep(10)
i += 1
except Timeout:
await asyncio.sleep(10)
i += 1
except RateLimitError:
await asyncio.sleep(10)
i += 1
except APIConnectionError as e:
print(e)
print(f"\n\n{bcolors.BOLD}{bcolors.FAIL}APIConnectionError. There is an issue with your internet connection. Please check your connection.{bcolors.ENDC}")
raise e
except InvalidRequestError as e:
print(e)
print(f"\n\n{bcolors.BOLD}{bcolors.FAIL}InvalidRequestError. Please check your request.{bcolors.ENDC}")
raise e
except AuthenticationError as e:
print(e)
print(f"\n\n{bcolors.BOLD}{bcolors.FAIL}AuthenticationError. Please check your API key.{bcolors.ENDC}")
raise e
except ServiceUnavailableError:
await asyncio.sleep(10)
i += 1
finally:
if i == 10:
print(f"\n\n{bcolors.BOLD}{bcolors.FAIL}OpenAI API is not responding. Please try again later.{bcolors.ENDC}")
raise TimeoutError
return response
openai = openai_caller()

150
utils/uploader.py Normal file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/python
'''Uploads a video to YouTube.'''
from http import client
import httplib2
import os
import random
import time
import json
import asyncio
import google.oauth2.credentials
import google_auth_oauthlib.flow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload
from google_auth_oauthlib.flow import InstalledAppFlow
httplib2.RETRIES = 1
MAX_RETRIES = 10
RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, client.NotConnected,
client.IncompleteRead, client.ImproperConnectionState,
client.CannotSendRequest, client.CannotSendHeader,
client.ResponseNotReady, client.BadStatusLine)
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]
CLIENT_SECRETS_FILE = ''
#SCOPES = ['https://www.googleapis.com/auth/youtube.upload', 'https://www.googleapis.com/upload/youtube/v3/thumbnails/set', 'https://www.googleapis.com/auth/youtube.force-ssl']
SCOPES = ['https://www.googleapis.com/auth/youtube']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'
VALID_PRIVACY_STATUSES = ('public', 'private', 'unlisted')
# Authorize the request and store authorization credentials.
async def get_authenticated_service(credentialsPath=""):
CLIENT_SECRETS_FILE=f'{credentialsPath}/client_secret.json'
if os.path.exists(f'{credentialsPath}/credentials.json'):
with open(f'{credentialsPath}/credentials.json') as json_file:
data = json.load(json_file)
credentials = google.oauth2.credentials.Credentials(
token=data['token'],
refresh_token=data['refresh_token'],
token_uri=data['token_uri'],
client_id=data['client_id'],
client_secret=data['client_secret'],
scopes=data['scopes']
)
else:
flow = InstalledAppFlow.from_client_secrets_file(
CLIENT_SECRETS_FILE, SCOPES)
credentials = flow.run_local_server(success_message="Heyy, yippie, you're authenticated ! You can close this window now !", authorization_prompt_message="Please authorize this app to upload videos on your YouTube account !")
with open(f'{credentialsPath}/credentials.json', 'w') as outfile:
outfile.write(credentials.to_json())
return build(API_SERVICE_NAME, API_VERSION, credentials=credentials)
async def initialize_upload(youtube, options):
tags = None
if options['keywords']:
tags = options['keywords'].split(',')
body = dict(
snippet=dict(
title=options['title'],
description=options['description'],
tags=tags,
categoryId=options['category'],
defaultLanguage='en'
),
status=dict(
privacyStatus=options['privacyStatus'],
selfDeclaredMadeForKids=False
)
)
# Call the API's videos.insert method to create and upload the video.
insert_request = youtube.videos().insert(
part=','.join(body.keys()),
body=body,
media_body=MediaFileUpload(options['file'], chunksize=-1, resumable=True)
)
videoid = resumable_upload(insert_request)
return videoid
async def resumable_upload(request):
response = None
error = None
retry = 0
while response is None:
try:
print('Uploading file...')
status, response = request.next_chunk()
if response is not None:
if 'id' in response:
print('Video id "%s" was successfully uploaded.' %
response['id'])
return response['id']
else:
exit('The upload failed with an unexpected response: %s' % response)
except HttpError as e:
if e.resp.status in RETRIABLE_STATUS_CODES:
error = 'A retriable HTTP error %d occurred:\n%s' % (e.resp.status,
e.content)
else:
raise
except RETRIABLE_EXCEPTIONS as e:
error = 'A retriable error occurred: %s' % e
if error is not None:
print(error)
retry += 1
if retry > MAX_RETRIES:
exit('No longer attempting to retry.')
max_sleep = 2 ** retry
sleep_seconds = random.random() * max_sleep
print('Sleeping %f seconds and then retrying...' % sleep_seconds)
await asyncio.sleep(sleep_seconds)
async def upload_video(path, title, description, category, keywords, privacyStatus='private', credentials_path=""):
options = {
'file': path +"/montage.mp4",
'title': title,
'description': description,
'category': category,
'keywords': keywords,
'privacyStatus': privacyStatus
}
youtube = await get_authenticated_service(credentials_path)
try:
videoid = await initialize_upload(youtube, options)
await upload_thumbnail(videoid, path + "/miniature.png", credentials_path)
return videoid
except HttpError as e:
print('An HTTP error %d occurred:\n%s' % (e.resp.status, e.content))
async def upload_thumbnail(video_id, file, credentials_path=""):
youtube = get_authenticated_service(credentials_path)
youtube.thumbnails().set( # type: ignore
videoId=video_id,
media_body=file
).execute()