mirror of
https://github.com/Paillat-dev/FABLE.git
synced 2026-01-02 01:06:20 +00:00
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:
35
utils/config.py
Normal file
35
utils/config.py
Normal 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
89
utils/misc.py
Normal 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
8
utils/normalize_file.py
Normal 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
110
utils/openaicaller.py
Normal 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
150
utils/uploader.py
Normal 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()
|
||||
Reference in New Issue
Block a user