Merge pull request from Derpy-Leggies/unstable

Unstable
This commit is contained in:
Michał 2023-09-27 14:00:31 +01:00 committed by GitHub
commit fd08fbd0c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1408 additions and 1226 deletions

View file

@ -1,3 +1,7 @@
> **IMPORTANT**
> This project is maintained irregularly and is still in rapid development.
> Things maybe here now, but not again in a few weeks
<div align="center"> <div align="center">
<img src=".github/images/OnlyLegs.png" width="200" height="200"/> <img src=".github/images/OnlyLegs.png" width="200" height="200"/>
<div id="user-content-toc"> <div id="user-content-toc">

View file

@ -3,7 +3,6 @@ Onlylegs - API endpoints
""" """
import os import os
import pathlib import pathlib
import re
import logging import logging
from uuid import uuid4 from uuid import uuid4
@ -23,88 +22,13 @@ from flask_login import login_required, current_user
from colorthief import ColorThief from colorthief import ColorThief
from onlylegs.extensions import db from onlylegs.extensions import db
from onlylegs.models import Users, Pictures, Exif from onlylegs.models import Pictures, Exif
from onlylegs.utils.generate_image import generate_thumbnail from onlylegs.utils.generate_image import generate_thumbnail
blueprint = Blueprint("api", __name__, url_prefix="/api") blueprint = Blueprint("api", __name__, url_prefix="/api")
@blueprint.route("/account/picture/<int:user_id>", methods=["POST"])
@login_required
def account_picture(user_id):
"""
Returns the profile of a user
"""
user = db.get_or_404(Users, user_id)
file = request.files.get("file", None)
# If no image is uploaded, return 404 error
if not file:
return jsonify({"error": "No file uploaded"}), 400
if user.id != current_user.id:
return jsonify({"error": "You are not allowed to do this, go away"}), 403
# Get file extension, generate random name and set file path
img_ext = pathlib.Path(file.filename).suffix.replace(".", "").lower()
img_name = str(user.id)
img_path = os.path.join(current_app.config["PFP_FOLDER"], img_name + "." + img_ext)
# Check if file extension is allowed
if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
logging.info("File extension not allowed: %s", img_ext)
return jsonify({"error": "File extension not allowed"}), 403
if user.picture:
# Delete cached files and old image
os.remove(os.path.join(current_app.config["PFP_FOLDER"], user.picture))
cache_name = user.picture.rsplit(".")[0]
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
cache_name + "*"
):
os.remove(cache_file)
# Save file
try:
file.save(img_path)
except OSError as err:
logging.info("Error saving file %s because of %s", img_path, err)
return jsonify({"error": "Error saving file"}), 500
img_colors = ColorThief(img_path).get_color()
# Save to database
user.colour = img_colors
user.picture = str(img_name + "." + img_ext)
db.session.commit()
return jsonify({"message": "File uploaded"}), 200
@blueprint.route("/account/username/<int:user_id>", methods=["POST"])
@login_required
def account_username(user_id):
"""
Returns the profile of a user
"""
user = db.get_or_404(Users, user_id)
new_name = request.form["name"]
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
# Validate the form
if not new_name or not username_regex.match(new_name):
return jsonify({"error": "Username is invalid"}), 400
if user.id != current_user.id:
return jsonify({"error": "You are not allowed to do this, go away"}), 403
# Save to database
user.username = new_name
db.session.commit()
return jsonify({"message": "Username changed"}), 200
@blueprint.route("/media/<path:path>", methods=["GET"]) @blueprint.route("/media/<path:path>", methods=["GET"])
def media(path): def media(path):
""" """

View file

@ -1,136 +1,179 @@
""" """
Onlylegs Gallery Onlylegs Gallery
This is the main app file, it loads all the other files and sets up the app This is the main app file, checks on app stability and runs all da shit
""" """
import os import os
import logging import logging
from flask import Flask, render_template, abort, request
from werkzeug.security import generate_password_hash
from werkzeug.exceptions import HTTPException
from flask_assets import Bundle from flask_assets import Bundle
from flask_migrate import init as migrate_init from flask_migrate import init as migrate_init
from flask import Flask, render_template, abort
from werkzeug.exceptions import HTTPException
from werkzeug.security import generate_password_hash
from onlylegs.extensions import db, migrate, login_manager, assets, compress, cache from onlylegs.extensions import db, migrate, login_manager, assets, compress, cache
from onlylegs.config import INSTANCE_DIR, MIGRATIONS_DIR from onlylegs.config import (
from onlylegs.models import Users INSTANCE_DIR,
from onlylegs.views import ( MIGRATIONS_DIR,
index as view_index, APPLICATION_ROOT,
image as view_image, DATABASE_NAME,
group as view_group,
settings as view_settings,
profile as view_profile,
) )
from onlylegs import api from onlylegs.models import Users
from onlylegs import auth as view_auth
from onlylegs import filters from onlylegs.views.index import blueprint as view_index
from onlylegs.views.image import blueprint as view_image
from onlylegs.views.group import blueprint as view_group
from onlylegs.views.settings import blueprint as view_settings
from onlylegs.views.profile import blueprint as view_profile
from onlylegs.api import blueprint as api
from onlylegs.auth import blueprint as view_auth
from onlylegs.filters import blueprint as filters
def set_logger():
file_name = os.path.join(APPLICATION_ROOT, "only.log")
logging_level = logging.INFO
date_format = "%Y-%m-%d %H:%M:%S"
log_format = "%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s"
logging.getLogger("werkzeug").disabled = True
logging.basicConfig(
filename=file_name,
level=logging_level,
datefmt=date_format,
format=log_format,
encoding="utf-8",
)
def create_db():
path_to_database = os.path.join(INSTANCE_DIR, DATABASE_NAME)
if not os.path.exists(path_to_database):
print("Database not found, creating...")
user = Users(
username=app.config["ADMIN_CONF"]["username"],
email=app.config["ADMIN_CONF"]["email"],
password=generate_password_hash("changeme!", method="scrypt"),
)
with app.app_context():
db.create_all()
db.session.add(user)
db.session.commit()
migrate_init(directory=MIGRATIONS_DIR)
print(
"####################################################",
"# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #",
'# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED, #',
"# PLEASE RESET IT IN THE SETTINGS! #",
"####################################################",
sep="\n",
)
return
print("Database found, continuing...")
def set_login_manager():
"""
LOGIN MANAGER
can also set session_protection to "strong"
this would protect against session hijacking
"""
login_manager.init_app(app)
login_manager.login_view = "onlylegs.index"
@login_manager.user_loader
def load_user(user_id):
return Users.query.filter_by(alt_id=user_id).first()
@login_manager.unauthorized_handler
def unauthorized():
error = 401
msg = "You are not authorized to view this page!!!!"
return render_template("error.html", error=error, msg=msg), error
def page_assets():
"""
ASSETS
bundles all the sass and js and minifies them
"""
assets.init_app(app)
page_scripts = Bundle(
"js/*.js", filters="jsmin", output="gen/main.js", depends="js/*.js"
)
page_styling = Bundle(
"sass/style.sass",
filters="libsass, cssmin",
output="gen/styles.css",
depends="sass/**/*.sass",
)
assets.register("scripts", page_scripts)
assets.register("styles", page_styling)
def handle_errors():
"""
ERROR HANDLER
handles all the errors and returns a nice error page
Code errors are displayed as 500 errors so no
sensitive information is leaked
"""
@app.errorhandler(Exception)
def error_page(err):
if not isinstance(err, HTTPException):
abort(500)
if request.method == "GET":
return (
render_template("error.html", error=err.code, msg=err.description),
err.code,
)
else:
return str(err.code) + ": " + err.description, err.code
def register_blueprints():
"""
BLUEPRINTS
registers all the blueprints
"""
app.register_blueprint(view_auth)
app.register_blueprint(view_index)
app.register_blueprint(view_image)
app.register_blueprint(view_group)
app.register_blueprint(view_profile)
app.register_blueprint(view_settings)
app.register_blueprint(api)
app.register_blueprint(filters)
app = Flask(__name__, instance_path=INSTANCE_DIR) app = Flask(__name__, instance_path=INSTANCE_DIR)
app.config.from_pyfile("config.py") app.config.from_pyfile("config.py")
# DATABASE
db.init_app(app) db.init_app(app)
migrate.init_app(app, db, directory=MIGRATIONS_DIR) migrate.init_app(app, db, directory=MIGRATIONS_DIR)
# If database file doesn't exist, create it create_db()
if not os.path.exists(os.path.join(INSTANCE_DIR, "gallery.sqlite3")):
print("Creating database")
with app.app_context():
db.create_all()
register_user = Users( set_logger()
username=app.config["ADMIN_CONF"]["username"], set_login_manager()
email=app.config["ADMIN_CONF"]["email"], page_assets()
password=generate_password_hash("changeme!", method="sha256"), handle_errors()
) register_blueprints()
db.session.add(register_user)
db.session.commit()
print(
"""
####################################################
# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #
# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED, #
# PLEASE UPDATE IT IN THE SETTINGS! #
####################################################
"""
)
# Check if migrations directory exists, if not create it
with app.app_context():
if not os.path.exists(MIGRATIONS_DIR):
print("Creating migrations directory")
migrate_init(directory=MIGRATIONS_DIR)
# LOGIN MANAGER
# can also set session_protection to "strong"
# this would protect against session hijacking
login_manager.init_app(app)
login_manager.login_view = "onlylegs.index"
@login_manager.user_loader
def load_user(user_id):
return Users.query.filter_by(alt_id=user_id).first()
@login_manager.unauthorized_handler
def unauthorized():
error = 401
msg = "You are not authorized to view this page!!!!"
return render_template("error.html", error=error, msg=msg), error
# ERROR HANDLERS
@app.errorhandler(Exception)
def error_page(err):
"""
Error handlers, if the error is not a HTTP error, return 500
"""
if not isinstance(err, HTTPException):
abort(500)
return (
render_template("error.html", error=err.code, msg=err.description),
err.code,
)
# ASSETS
assets.init_app(app)
scripts = Bundle(
"js/*.js", output="gen/js.js", depends="js/*.js"
) # filter jsmin is broken :c
styles = Bundle(
"sass/style.sass",
filters="libsass, cssmin",
output="gen/styles.css",
depends="sass/**/*.sass",
)
assets.register("scripts", scripts)
assets.register("styles", styles)
# BLUEPRINTS
app.register_blueprint(view_auth.blueprint)
app.register_blueprint(view_index.blueprint)
app.register_blueprint(view_image.blueprint)
app.register_blueprint(view_group.blueprint)
app.register_blueprint(view_profile.blueprint)
app.register_blueprint(view_settings.blueprint)
app.register_blueprint(api.blueprint)
# FILTERS
app.register_blueprint(filters.blueprint)
# CACHE AND COMPRESS
cache.init_app(app) cache.init_app(app)
compress.init_app(app) compress.init_app(app)
# Yupee! We got there :3
print("Done!")
logging.info("Gallery started successfully!") logging.info("Gallery started successfully!")

View file

@ -105,5 +105,5 @@ def logout():
Clear the current session, including the stored user id Clear the current session, including the stored user id
""" """
logout_user() logout_user()
flash(["Goodbye!!!", "4"]) flash("Goodbye!!!", "4")
return redirect(url_for("gallery.index")) return redirect(url_for("gallery.index"))

View file

@ -6,42 +6,43 @@ import platformdirs
import importlib.metadata import importlib.metadata
from dotenv import load_dotenv from dotenv import load_dotenv
from yaml import safe_load from yaml import safe_load
from utils import startup
# App Sanity Checks
startup.check_dirs()
startup.check_env()
startup.check_conf()
# Set dirs # Set dirs
user_dir = platformdirs.user_config_dir("onlylegs") APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs")
instance_dir = os.path.join(user_dir, "instance") UPLOAD_FOLDER = os.path.join(APPLICATION_ROOT, "media", "uploads")
MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
CACHE_FOLDER = os.path.join(APPLICATION_ROOT, "media", "cache")
PFP_FOLDER = os.path.join(APPLICATION_ROOT, "media", "pfp")
BANNER_FOLDER = os.path.join(APPLICATION_ROOT, "media", "banner")
# Load environment variables # Load env and config files
# print("Loading environment variables...") load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
load_dotenv(os.path.join(user_dir, ".env"))
# Load config from user dir config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
# print("Loading config...") with open(config_file, encoding="utf-8", mode="r") as file:
with open(os.path.join(user_dir, "conf.yml"), encoding="utf-8", mode="r") as file:
conf = safe_load(file) conf = safe_load(file)
# Flask config # Flask config
SECRET_KEY = os.environ.get("FLASK_SECRET") SECRET_KEY = os.environ.get("FLASK_SECRET")
SQLALCHEMY_DATABASE_URI = "sqlite:///gallery.sqlite3"
MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"] MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"]
ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"] ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"]
APP_VERSION = importlib.metadata.version("OnlyLegs")
# Database
DATABASE_NAME = "gallery.sqlite3"
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE_NAME
INSTANCE_DIR = os.path.join(APPLICATION_ROOT, "instance")
MIGRATIONS_DIR = os.path.join(INSTANCE_DIR, "migrations")
# Pass YAML config to app # Pass YAML config to app
ADMIN_CONF = conf["admin"] ADMIN_CONF = conf["admin"]
UPLOAD_CONF = conf["upload"] UPLOAD_CONF = conf["upload"]
WEBSITE_CONF = conf["website"] WEBSITE_CONF = conf["website"]
# Directories
UPLOAD_FOLDER = os.path.join(user_dir, "media", "uploads")
CACHE_FOLDER = os.path.join(user_dir, "media", "cache")
PFP_FOLDER = os.path.join(user_dir, "media", "pfp")
MEDIA_FOLDER = os.path.join(user_dir, "media")
# Database
INSTANCE_DIR = instance_dir
MIGRATIONS_DIR = os.path.join(INSTANCE_DIR, "migrations")
# App
APP_VERSION = importlib.metadata.version("OnlyLegs")

View file

@ -4,6 +4,7 @@ Custom Jinja2 filters
""" """
from flask import Blueprint from flask import Blueprint
from onlylegs.utils import colour as colour_utils from onlylegs.utils import colour as colour_utils
import colorsys
blueprint = Blueprint("filters", __name__) blueprint = Blueprint("filters", __name__)
@ -17,4 +18,32 @@ def colour_contrast(colour):
"color: var(--fg-white);" or "color: var(--fg-black);" "color: var(--fg-white);" or "color: var(--fg-black);"
""" """
colour_obj = colour_utils.Colour(colour) colour_obj = colour_utils.Colour(colour)
return "rgb(var(--fg-black));" if colour_obj.is_light() else "rgb(var(--fg-white));" return (
"var(--foreground-black);"
if colour_obj.is_light()
else "var(--foreground-white);"
)
@blueprint.app_template_filter()
def hsl_hue(rgb):
"""
Pass in a rgb value and will return the hue value
"""
r, g, b = rgb
r /= 255
g /= 255
b /= 255
return colorsys.rgb_to_hls(r, g, b)[0] * 360
@blueprint.app_template_filter()
def hsl_saturation(rgb):
"""
Pass in a rgb value and will return the saturation value
"""
r, g, b = rgb
r /= 255
g /= 255
b /= 255
return colorsys.rgb_to_hls(r, g, b)[1] * 100

View file

@ -64,7 +64,7 @@ class Exif(db.Model):
picture_id = db.Column(db.Integer, db.ForeignKey("pictures.id")) picture_id = db.Column(db.Integer, db.ForeignKey("pictures.id"))
key = db.Column(db.String, nullable=False) key = db.Column(db.String, nullable=False)
value = db.Column(db.String, nullable=False) value = db.Column(db.PickleType, nullable=False)
class Albums(db.Model): class Albums(db.Model):

View file

@ -14,47 +14,6 @@ function imageFullscreen() {
} }
function imageShowOptionsPopup(obj) { function imageShowOptionsPopup(obj) {
// let title = 'Options';
// let subtitle = null;
//
// let body = document.createElement('div');
// body.style.cssText = 'display: flex; flex-direction: column; gap: 0.5rem;';
//
// let copyBtn = document.createElement('button');
// copyBtn.classList.add('btn-block');
// copyBtn.innerHTML = 'Copy URL';
// copyBtn.onclick = () => {
// copyToClipboard(window.location.href)
// }
//
// let downloadBtn = document.createElement('a');
// downloadBtn.classList.add('btn-block');
// downloadBtn.innerHTML = 'Download';
// downloadBtn.href = '/api/media/uploads/' + image_data["filename"];
// downloadBtn.download = '';
//
// body.appendChild(copyBtn);
// body.appendChild(downloadBtn);
//
// if (image_data["owner"]) {
// let editBtn = document.createElement('button');
// editBtn.classList.add('btn-block');
// editBtn.classList.add('critical');
// editBtn.innerHTML = 'Edit';
// editBtn.onclick = imageEditPopup;
//
// let deleteBtn = document.createElement('button');
// deleteBtn.classList.add('btn-block');
// deleteBtn.classList.add('critical');
// deleteBtn.innerHTML = 'Delete';
// deleteBtn.onclick = imageDeletePopup;
//
// body.appendChild(editBtn);
// body.appendChild(deleteBtn);
// }
//
// popupShow(title, subtitle, body, [popupCancelButton]);
showContextMenu(obj, [ showContextMenu(obj, [
{ {
'value': 'Edit', 'value': 'Edit',
@ -62,7 +21,8 @@ function imageShowOptionsPopup(obj) {
dissmissContextMenu(); dissmissContextMenu();
imageEditPopup(); imageEditPopup();
}, },
'type': 'critical' 'type': 'critical',
'icon': '<i class="ph-fill ph-pencil"></i>'
}, },
{ {
'value': 'Delete', 'value': 'Delete',
@ -70,7 +30,8 @@ function imageShowOptionsPopup(obj) {
dissmissContextMenu(); dissmissContextMenu();
imageDeletePopup(); imageDeletePopup();
}, },
'type': 'critical' 'type': 'critical',
'icon': '<i class="ph-fill ph-trash"></i>'
} }
], 'button') ], 'button')
} }

View file

@ -141,41 +141,14 @@ function clearUpload() {
} }
// function createJob(file) {
// jobContainer = document.createElement("div");
// jobContainer.classList.add("job");
// jobStatus = document.createElement("span");
// jobStatus.classList.add("job__status");
// jobStatus.innerHTML = "Uploading...";
// jobProgress = document.createElement("span");
// jobProgress.classList.add("progress");
// jobImg = document.createElement("img");
// jobImg.src = URL.createObjectURL(file);
// jobImgFilter = document.createElement("span");
// jobImgFilter.classList.add("img-filter");
// jobContainer.appendChild(jobStatus);
// jobContainer.appendChild(jobProgress);
// jobContainer.appendChild(jobImg);
// jobContainer.appendChild(jobImgFilter);
// return jobContainer;
// }
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Function to upload images // Function to upload images
const uploadTab = document.querySelector(".upload-panel"); const uploadTab = document.querySelector(".upload-panel");
if (!uploadTab) { return; } // If upload tab doesn't exist, don't run this code :3 if (!uploadTab) { return }
const uploadTabDrag = uploadTab.querySelector("#dragIndicator"); const uploadTabDrag = uploadTab.querySelector("#dragIndicator");
const uploadForm = uploadTab.querySelector('#uploadForm'); const uploadForm = uploadTab.querySelector('#uploadForm');
// let jobList = document.querySelector(".upload-jobs");
const fileDrop = uploadForm.querySelector('.fileDrop-block'); const fileDrop = uploadForm.querySelector('.fileDrop-block');
const fileDropTitle = fileDrop.querySelector('.status'); const fileDropTitle = fileDrop.querySelector('.status');
@ -228,54 +201,6 @@ document.addEventListener('DOMContentLoaded', () => {
formData.append("description", fileDescription.value); formData.append("description", fileDescription.value);
formData.append("tags", fileTags.value); formData.append("tags", fileTags.value);
// jobItem = createJob(fileUpload.files[0]);
// jobStatus = jobItem.querySelector(".job__status");
// Upload the information
// $.ajax({
// url: '/api/upload',
// type: 'post',
// data: formData,
// contentType: false,
// processData: false,
// beforeSend: function () {
// // Add job to list
// jobList.appendChild(jobItem);
// },
// success: function (response) {
// jobItem.classList.add("success");
// jobStatus.innerHTML = "Uploaded successfully";
// if (!document.querySelector(".upload-panel").classList.contains("open")) {
// addNotification("Image uploaded successfully", 1);
// }
// },
// error: function (response) {
// jobItem.classList.add("critical");
// switch (response.status) {
// case 500:
// jobStatus.innerHTML = "Server exploded, F's in chat";
// break;
// case 400:
// case 404:
// jobStatus.innerHTML = "Error uploading. Blame yourself";
// break;
// case 403:
// jobStatus.innerHTML = "None but devils play past here...";
// break;
// case 413:
// jobStatus.innerHTML = "File too large!!!!!!";
// break;
// default:
// jobStatus.innerHTML = "Error uploading file, blame someone";
// break;
// }
// if (!document.querySelector(".upload-panel").classList.contains("open")) {
// addNotification("Error uploading file", 2);
// }
// },
// });
fetch('/api/media/upload', { fetch('/api/media/upload', {
method: 'POST', method: 'POST',
body: formData body: formData

View file

@ -2,7 +2,7 @@
.banner-small .banner-small
width: 100% width: 100%
position: relative position: relative
color: RGB($fg-white) color: var(--foreground-white)
.link .link
padding: 0.1rem 0.3rem padding: 0.1rem 0.3rem
@ -10,15 +10,15 @@
text-decoration: none text-decoration: none
font-weight: 500 font-weight: 500
background-color: RGB($fg-white) background-color: var(--foreground-white)
color: RGB($fg-black) color: var(--foreground-black)
border-radius: $rad-inner border-radius: calc(var(--radius) / 2)
cursor: pointer cursor: pointer
&:hover &:hover
background-color: RGB($fg-black) background-color: var(--foreground-black)
color: RGB($fg-white) color: var(--foreground-white)
.banner .banner
height: 30rem height: 30rem
@ -26,7 +26,8 @@
img img
position: absolute position: absolute
inset: 0 top: 0
left: 0
width: 100% width: 100%
height: 100% height: 100%
@ -43,7 +44,7 @@
width: 100% width: 100%
height: 100% height: 100%
background: linear-gradient(to right, RGB($bg-100), transparent) background: linear-gradient(to right, var(--background-100), transparent 80%, var(--background-100) 100%)
z-index: +1 z-index: +1
@ -79,7 +80,7 @@
font-size: 6.9rem font-size: 6.9rem
font-weight: 700 font-weight: 700
color: RGB($primary) color: var(--primary)
.banner-info .banner-info
grid-area: info grid-area: info
@ -104,8 +105,8 @@
width: 6.9rem width: 6.9rem
height: 6.9rem height: 6.9rem
background-color: RGB($primary) background-color: var(--primary)
border-radius: $rad border-radius: var(--rad)
overflow: hidden overflow: hidden
.banner-small .banner-small
@ -144,7 +145,7 @@
font-weight: 700 font-weight: 700
font-size: 1.5rem font-size: 1.5rem
color: RGB($primary) color: var(--primary)
.banner-info .banner-info
margin-right: 0.6rem margin-right: 0.6rem
@ -156,7 +157,7 @@
margin-left: auto margin-left: auto
width: auto width: auto
@media (max-width: $breakpoint) @media (max-width: 800px)
.banner, .banner,
.banner-small .banner-small
&::after &::after
@ -168,7 +169,7 @@
max-height: 30vh max-height: 30vh
.banner-filter .banner-filter
background: linear-gradient(to bottom, RGB($bg-100), transparent) background: linear-gradient(to top, var(--background-100), transparent)
.banner-content .banner-content
padding: 0.5rem padding: 0.5rem
@ -192,7 +193,7 @@
display: none display: none
.pill-row .pill-row
margin-top: 0rem margin-top: 0
.banner-picture .banner-picture
margin: 0 auto margin: 0 auto

View file

@ -1,14 +1,3 @@
@mixin btn-block($color)
background-color: RGBA($color, 0.1)
color: RGB($color)
// box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2)
&:hover, &:focus-visible
background-color: RGBA($color, 0.15)
color: RGB($color)
// box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($color, 0.2)
.btn-block .btn-block
padding: 0.4rem 0.7rem padding: 0.4rem 0.7rem
@ -26,19 +15,17 @@
font-weight: 400 font-weight: 400
text-align: center text-align: center
background-color: RGBA($white, 0.1) background-color: var(--white-transparent)
color: RGB($white) color: var(--white)
border: none border: none
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
// box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2)
outline: none outline: none
cursor: pointer cursor: pointer
transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out, box-shadow 0.15s ease-in-out transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out, box-shadow 0.15s ease-in-out
&:hover, &:focus-visible &:hover, &:focus-visible
background-color: RGBA($white, 0.2) background-color: var(--white-transparent)
// box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3)
&.transparent &.transparent
background-color: transparent background-color: transparent
@ -47,20 +34,50 @@
text-decoration: underline text-decoration: underline
&.primary &.primary
@include btn-block($primary) background-color: var(--primary-transparent)
&.critical color: var(--primary)
@include btn-block($critical)
&.warning &:hover, &:focus-visible
@include btn-block($warning) background-color: var(--primary)
color: var(--black)
&.success &.success
@include btn-block($success) background-color: var(--success-transparent)
color: var(--success)
&:hover, &:focus-visible
background-color: var(--success)
color: var(--black)
&.warning
background-color: var(--warning-transparent)
color: var(--warning)
&:hover, &:focus-visible
background-color: var(--warning)
color: var(--black)
&.critical
background-color: var(--danger-transparent)
color: var(--danger)
&:hover, &:focus-visible
background-color: var(--danger)
color: var(--black)
&.info &.info
@include btn-block($info) background-color: var(--info-transparent)
color: var(--info)
&:hover, &:focus-visible
background-color: var(--info)
color: var(--black)
&.black &.black
@include btn-block($black) background-color: var(--black-transparent)
color: var(--white)
&:hover, &:focus-visible
background-color: var(--black)
color: var(--white)
&.disabled, &:disabled &.disabled, &:disabled
color: RGB($fg-dim) color: var(--foreground-gray)
cursor: unset cursor: unset
.input-checkbox .input-checkbox
@ -77,7 +94,7 @@
font-weight: 400 font-weight: 400
text-align: left text-align: left
color: RGB($fg-white) color: var(--foreground-white)
.input-block .input-block
padding: 0.4rem 0.7rem padding: 0.4rem 0.7rem
@ -95,28 +112,32 @@
font-weight: 400 font-weight: 400
text-align: left text-align: left
background-color: RGBA($white, 0.1) background-color: var(--white-transparent)
color: RGB($white) color: var(--white)
border: none border: none
border-bottom: 3px solid RGBA($white, 0.1) border-bottom: 3px solid var(--white-transparent)
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
// box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2)
outline: none outline: none
cursor: pointer cursor: pointer
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out
&:not(:focus):not([value=""]):not(:placeholder-shown) &:not(:focus):not([value=""]):not(:placeholder-shown)
border-color: RGBA($white, 0.3) border-color: var(--white-transparent)
&:hover &:hover
border-color: RGBA($white, 0.3) border-color: var(--white-transparent)
&:focus &:focus
border-color: RGB($primary) border-color: var(--primary)
&.black &.black
@include btn-block($black) background-color: var(--black-transparent)
color: var(--white)
&:hover, &:focus-visible
background-color: var(--black)
color: var(--white)
.fileDrop-block .fileDrop-block
padding: 1rem 1.25rem padding: 1rem 1.25rem
@ -136,11 +157,10 @@
font-weight: 400 font-weight: 400
text-align: center text-align: center
background-color: RGBA($white, 0.1) background-color: var(--white-transparent)
color: RGB($white) color: var(--white)
border: none border: none
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
// box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2)
outline: none outline: none
cursor: pointer cursor: pointer
@ -164,24 +184,21 @@
overflow: hidden overflow: hidden
&:hover, &:focus-visible &:hover, &:focus-visible
background-color: RGBA($white, 0.2) background-color: var(--white-transparent)
color: RGB($white) color: var(--white)
// box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3)
&.active &.active
background-color: RGBA($primary, 0.2) background-color: var(--primary-transparent)
color: RGB($primary) color: var(--primary)
// box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($primary, 0.3)
&.edging &.edging
background-color: RGBA($white, 0.2) background-color: var(--white-transparent)
color: RGB($white) color: var(--white)
// box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3)
input input
display: none // So it doesnt get in the way of the drop as that breaks things // So it doesnt get in the way of the drop as that breaks things
display: none
&.error &.error
background-color: RGBA($critical, 0.2) background-color: var(--danger)
color: RGB($critical) color: var(--black)
// box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($critical, 0.3)

View file

@ -1,39 +0,0 @@
.info-button
margin: 0
padding: 0
width: auto
height: auto
position: fixed
bottom: 0.75rem
right: -3rem
display: flex
justify-content: center
align-items: center
background-color: RGB($bg-300)
color: RGB($fg-white)
border-radius: $rad
border: none
opacity: 0
z-index: 20
cursor: pointer
transition: all 0.2s cubic-bezier(.86, 0, .07, 1)
i
margin: 0.5rem
font-size: 1.25rem
&:hover
color: RGB($info)
&.show
right: 0.75rem
opacity: 1
@media (max-width: $breakpoint)
.info-button
bottom: 4.25rem

View file

@ -16,9 +16,8 @@
display: flex display: flex
background-color: RGB($bg-200) background-color: var(--background-200)
border-radius: $rad border-radius: var(--rad)
// box-shadow: 0 1px 0 RGB($bg-100), 0 -1px 0 RGB($bg-300)
.pill-text .pill-text
margin: 0 margin: 0
@ -37,9 +36,9 @@
font-size: 1rem font-size: 1rem
font-weight: 400 font-weight: 400
background-color: RGB($bg-200) background-color: var(--background-200)
color: RGB($fg-white) color: var(--foreground-white)
border-radius: $rad border-radius: var(--rad)
.pill-item .pill-item
margin: 0 margin: 0
@ -58,42 +57,41 @@
border: none border: none
background-color: transparent background-color: transparent
color: RGB($fg-white) color: var(--foreground-white)
i i
font-size: 1.25rem font-size: 1.25rem
&:hover &:hover
cursor: pointer cursor: pointer
color: var(--primary)
color: RGB($primary) &:disabled, &[disabled], &.disabled
color: var(--foreground-gray)
&.disabled, &:disabled cursor: default
color: RGB($fg-dim)
cursor: unset
.pill__critical .pill__critical
color: RGB($critical) color: var(--danger)
span span
background: RGB($critical) background: var(--danger)
color: RGB($fg-white) color: var(--foreground-white)
i i
color: RGB($critical) color: var(--danger)
&:hover &:hover
color: RGB($fg-white) color: var(--foreground-white)
.pill__info .pill__info
color: RGB($info) color: var(--info)
span span
color: RGB($info) color: var(--info)
&:hover &:hover
color: RGB($fg-white) color: var(--foreground-white)
@media (max-width: $breakpoint) @media (max-width: var(--breakpoint))
.tool-tip .tool-tip
display: none display: none

View file

@ -13,9 +13,9 @@
justify-content: center justify-content: center
align-items: center align-items: center
background-color: RGB($bg-300) background-color: var(--background-100)
color: RGB($fg-white) color: var(--foreground-white)
border-radius: $rad border-radius: calc(var(--rad) / 2)
border: none border: none
opacity: 0 opacity: 0
@ -28,12 +28,12 @@
font-size: 1.25rem font-size: 1.25rem
&:hover &:hover
color: RGB($primary) color: var(--primary)
&.show &.show
right: 0.75rem right: 0.75rem
opacity: 1 opacity: 1
@media (max-width: $breakpoint) @media (max-width: 800px)
.top-of-page .top-of-page
bottom: 4.25rem bottom: 4.25rem

View file

@ -25,13 +25,13 @@
align-items: flex-start align-items: flex-start
gap: 0.25rem gap: 0.25rem
background-color: RGB($bg-300) background-color: var(--background-300)
border: 1px solid RGB($bg-200) border: 1px solid var(--background-200)
border-radius: 6px border-radius: 6px
overflow: hidden overflow: hidden
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out transition: transform 0.35s var(--animation-bounce), opacity 0.35s var(--animation-bounce)
transform-origin: center center transform-origin: center center
opacity: 0.5 opacity: 0.5
transform: scale(0, 0) transform: scale(0, 0)
@ -46,7 +46,7 @@
text-align: center text-align: center
font-size: 1.2rem font-size: 1.2rem
font-weight: 400 font-weight: 400
color: RGB($fg-white) color: var(--foreground-white)
.contextMenuItem .contextMenuItem
margin: 0 margin: 0
@ -61,22 +61,22 @@
align-items: center align-items: center
gap: 0.5rem gap: 0.5rem
background-color: RGB($bg-300) background-color: var(--background-300)
color: RGB($fg-white) color: var(--foreground-white)
border: none border: none
border-radius: 3px border-radius: 3px
cursor: pointer cursor: pointer
.contextMenuItem:hover .contextMenuItem:hover
background-color: RGB($bg-200) background-color: var(--background-200)
.contextMenuItem__critical .contextMenuItem__critical
color: RGB($critical) color: var(--danger)
.contextMenuItem__warning .contextMenuItem__warning
color: RGB($warning) color: var(--warning)
.contextMenuItem__success .contextMenuItem__success
color: RGB($success) color: var(--success)
.contextMenuItem__info .contextMenuItem__info
color: RGB($primary) color: var(--primary)
.contextMenuText .contextMenuText
margin: 0 margin: 0
@ -104,7 +104,7 @@
height: 1px height: 1px
border: none border: none
background-color: RGB($bg-200) background-color: var(--background-200)
.contextMenu__show .contextMenu__show
opacity: 1 opacity: 1

View file

@ -12,26 +12,21 @@
font-weight: 700 font-weight: 700
.gallery-grid .gallery-grid
margin: 0
padding: 0.35rem
width: 100% width: 100%
display: grid display: grid
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
gap: 0.5rem
.gallery-item .gallery-item
margin: 0.35rem
padding: 0
position: relative position: relative
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
box-shadow: 0 0.15rem 0.4rem 0.1rem RGBA($bg-100, 0.4) box-shadow: 0 0.15rem 0.4rem 0.1rem var(--black-transparent)
box-sizing: border-box box-sizing: border-box
overflow: hidden overflow: hidden
transition: box-shadow 0.2s cubic-bezier(.79, .14, .15, .86) transition: box-shadow 0.2s var(--animation-smooth)
.image-filter .image-filter
margin: 0 margin: 0
@ -48,11 +43,11 @@
flex-direction: column flex-direction: column
justify-content: flex-end justify-content: flex-end
background-image: linear-gradient(to top, rgba($bg-100, 0.69), transparent) background-image: linear-gradient(to top, var(--black-transparent), transparent)
opacity: 0 // hide opacity: 0 // hide
z-index: +4 z-index: +4
transition: opacity 0.2s cubic-bezier(.79, .14, .15, .86) transition: opacity 0.2s var(--animation-smooth)
.image-title, .image-title,
.image-subtitle .image-subtitle
@ -63,8 +58,8 @@
text-overflow: ellipsis text-overflow: ellipsis
overflow: hidden overflow: hidden
color: RGB($fg-white) color: var(--foreground-white)
text-shadow: 0px 0px 2px RGB($fg-black) text-shadow: 0 0 2px var(--foreground-black)
.image-title .image-title
font-size: 0.9rem font-size: 0.9rem
@ -84,21 +79,20 @@
object-fit: cover object-fit: cover
object-position: center object-position: center
background-color: RGB($bg-bright) background-color: var(--background-bright)
&:hover &:hover
box-shadow: 0 0.2rem 0.4rem 0.1rem RGBA($bg-100, 0.6) box-shadow: 0 0.2rem 0.4rem 0.1rem var(--black-transparent)
.image-filter .image-filter
opacity: 1 opacity: 1
.group-item .group-item
margin: 0.35rem
padding: 0 padding: 0
position: relative position: relative
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
box-sizing: border-box box-sizing: border-box
overflow: hidden overflow: hidden
@ -119,7 +113,7 @@
flex-direction: column flex-direction: column
justify-content: flex-end justify-content: flex-end
background-image: linear-gradient(to top, rgba($bg-100, 0.8), transparent) background-image: linear-gradient(to top, var(--black-transparent), transparent)
z-index: +4 z-index: +4
@ -132,8 +126,8 @@
text-overflow: ellipsis text-overflow: ellipsis
overflow: hidden overflow: hidden
color: RGB($fg-white) color: var(--foreground-white)
text-shadow: 0px 0px 2px RGB($fg-black) text-shadow: 0 0 2px var(--foreground-black)
.image-title .image-title
font-size: 0.9rem font-size: 0.9rem
@ -165,11 +159,11 @@
object-fit: cover object-fit: cover
object-position: center object-position: center
background-color: RGB($bg-bright) background-color: var(--background-800)
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
box-shadow: 0 0 0.4rem 0.25rem RGBA($bg-100, 0.1) box-shadow: 0 0 0.4rem 0.25rem var(--black-transparent)
transition: transform 0.2s cubic-bezier(.79, .14, .15, .86) transition: transform 0.2s var(--animation-smooth)
&.size-1 &.size-1
.data-1 .data-1
@ -224,5 +218,5 @@
grid-template-columns: auto auto auto grid-template-columns: auto auto auto
.gallery-item .gallery-item
margin: 0.35rem margin: 0.1rem
position: relative position: relative

View file

@ -1,11 +1,11 @@
.info-container .info-container
padding: 0.5rem 0 0 0.5rem padding: 0.5rem 0 0.5rem 0.5rem
width: 27rem width: 27rem
position: absolute position: absolute
top: 0 top: 0
left: 0 left: 0
bottom: 0 bottom: 0
background-image: linear-gradient(90deg, $bg-transparent, transparent) background-image: linear-gradient(90deg, var(--background-shade), transparent)
overflow-y: auto overflow-y: auto
transition: left 0.3s cubic-bezier(0.76, 0, 0.17, 1) transition: left 0.3s cubic-bezier(0.76, 0, 0.17, 1)
z-index: 2 z-index: 2
@ -18,7 +18,7 @@
left: -27rem left: -27rem
@media (max-width: 1100px) @media (max-width: 1100px)
.info-container .info-container
padding: 0 0.5rem 0 0.5rem padding: 0
width: 100% width: 100%
position: relative position: relative
background: none background: none
@ -32,9 +32,9 @@ details
padding: 0.5rem padding: 0.5rem
display: flex display: flex
flex-direction: column flex-direction: column
background-color: RGB($bg-300) background-color: var(--background-300)
color: RGB($fg-white) color: var(--foreground-white)
border-radius: $rad border-radius: var(--rad)
overflow: hidden overflow: hidden
summary summary
@ -44,7 +44,7 @@ details
justify-content: flex-start justify-content: flex-start
align-items: center align-items: center
position: relative position: relative
color: RGB($primary) color: var(--primary)
> i > i
margin-right: 0 margin-right: 0
@ -58,13 +58,6 @@ details
font-size: 1.1rem font-size: 1.1rem
font-weight: 500 font-weight: 500
&[open]
summary
margin-bottom: 0.5rem
> i.collapse-indicator
transform: rotate(90deg)
p p
margin: 0 margin: 0
padding: 0 padding: 0
@ -79,7 +72,7 @@ details
margin: 0 margin: 0
padding: 0 padding: 0
color: RGB($primary) color: var(--primary)
cursor: pointer cursor: pointer
text-decoration: none text-decoration: none
@ -91,7 +84,7 @@ details
width: 1.1rem width: 1.1rem
height: 1.1rem height: 1.1rem
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
object-fit: cover object-fit: cover
@ -137,6 +130,16 @@ details
tr:last-of-type td tr:last-of-type td
padding-bottom: 0 padding-bottom: 0
&[open]
summary
margin-bottom: 0.5rem
> i.collapse-indicator
transform: rotate(90deg)
&:last-of-type
margin-bottom: 0
.img-colours .img-colours
width: 100% width: 100%
@ -154,7 +157,7 @@ details
justify-content: center justify-content: center
align-items: center align-items: center
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
border: none border: none
i i
@ -197,7 +200,9 @@ details
max-height: 100% max-height: 100%
object-fit: contain object-fit: contain
object-position: center object-position: center
border-radius: $rad border-radius: var(--rad)
transition: border-radius 0.3s cubic-bezier(0.76, 0, 0.17, 1)
&.collapsed &.collapsed
padding: 0 padding: 0
@ -207,6 +212,7 @@ details
border-radius: 0 border-radius: 0
@media (max-width: 1100px) @media (max-width: 1100px)
.image-container .image-container
padding: 0 0 0.5rem 0
position: relative position: relative
left: 0 left: 0
@ -218,17 +224,17 @@ details
max-height: 69vh max-height: 69vh
&.collapsed &.collapsed
padding: 0.5rem padding: 0 0 0.5rem 0
left: 0 left: 0
picture img picture img
border-radius: $rad border-radius: var(--rad)
.background .background
position: absolute position: absolute
inset: 0 inset: 0
background-color: RGB($bg-300) background-color: var(--background-300)
background-image: linear-gradient(to right, RGB($bg-400) 15%, RGB($bg-200) 35%, RGB($bg-400) 50%) background-image: linear-gradient(to right, var(--background-400) 15%, var(--background-200) 35%, var(--background-400) 50%)
background-size: 1000px 640px background-size: 1000px 640px
animation: imgLoading 1.8s linear infinite forwards animation: imgLoading 1.8s linear infinite forwards
user-select: none user-select: none
@ -240,7 +246,7 @@ details
inset: 0 inset: 0
width: 100% width: 100%
height: 100% height: 100%
background-color: RGB($fg-white) background-color: var(--foreground-white)
filter: blur(3rem) saturate(1.2) brightness(0.7) filter: blur(3rem) saturate(1.2) brightness(0.7)
transform: scale(1.1) transform: scale(1.1)
object-fit: cover object-fit: cover

View file

@ -42,27 +42,27 @@ nav
> i > i
padding: 0.5rem padding: 0.5rem
font-size: 1.3rem font-size: 1.3rem
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
color: inherit color: inherit
> .nav-pfp > .nav-pfp
padding: 0.4rem padding: 0.4rem
width: 2.3rem width: 2.3rem
height: 2.3rem height: 2.3rem
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
img img
width: 100% width: 100%
height: 100% height: 100%
object-fit: cover object-fit: cover
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
&:hover &:hover
> i, .nav-pfp > i, .nav-pfp
background: RGBA($fg-white, 0.1) background: var(--white-transparent)
&.selected &.selected
color: RGB($primary) color: var(--primary)
&::before &::before
content: '' content: ''
@ -76,9 +76,9 @@ nav
height: calc(100% - 1rem) height: calc(100% - 1rem)
background-color: currentColor background-color: currentColor
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
@media (max-width: $breakpoint) @media (max-width: 800px)
nav nav
width: 100vw width: 100vw
height: 3.5rem height: 3.5rem
@ -91,7 +91,7 @@ nav
bottom: 0 bottom: 0
left: 0 left: 0
background-color: RGB($background) background-color: var(--background-100)
> span > span
display: none display: none

View file

@ -1,23 +1,9 @@
@keyframes notificationTimeout @keyframes notificationTimeout
0% 0%
left: -100% width: 0
height: 3px
90%
left: 0%
height: 3px
95%
left: 0%
height: 0
100% 100%
left: 0% width: 100%
height: 0
@mixin notification($color)
color: RGB($color)
&::after
background-color: RGB($color)
.notifications .notifications
margin: 0 margin: 0
padding: 0 padding: 0
@ -46,9 +32,9 @@
position: relative position: relative
background-color: RGB($bg-300) background-color: var(--background-400)
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
color: RGB($fg-white) color: var(--foreground-white)
opacity: 0 opacity: 0
transform: scale(0.8) transform: scale(0.8)
@ -60,26 +46,34 @@
&::after &::after
content: "" content: ""
width: 100% width: 0
height: 3px height: 3px
position: absolute position: absolute
bottom: 0px bottom: 0
left: 0px left: 0
background-color: RGB($fg-white) background-color: var(--foreground-white)
z-index: +2 z-index: +2
animation: notificationTimeout 5.1s linear animation: notificationTimeout 5.1s ease-out forwards
&.success &.success
@include notification($success) color: var(--success)
&::after
background-color: var(--success)
&.warning &.warning
@include notification($warning) color: var(--warning)
&::after
background-color: var(--warning)
&.critical &.critical
@include notification($critical) color: var(--danger)
&::after
background-color: var(--danger)
&.info &.info
@include notification($info) color: var(--info)
&::after
background-color: var(--info)
&.show &.show
opacity: 1 opacity: 1
@ -89,7 +83,7 @@
margin: 0 margin: 0
max-height: 0 max-height: 0
opacity: 0 opacity: 0
transform: translateX(100%) transform: translateY(1rem)
transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out
.sniffle__notification-icon .sniffle__notification-icon
@ -103,7 +97,7 @@
justify-content: center justify-content: center
align-items: center align-items: center
background-color: RGB($bg-200) background-color: var(--background-300)
i i
font-size: 1.25rem font-size: 1.25rem
@ -125,7 +119,7 @@
line-height: 1 line-height: 1
text-align: left text-align: left
@media (max-width: $breakpoint) @media (max-width: 800px)
.notifications .notifications
bottom: 3.8rem bottom: 3.8rem
width: calc(100vw - 0.6rem) width: calc(100vw - 0.6rem)
@ -133,10 +127,6 @@
.sniffle__notification .sniffle__notification
width: 100% width: 100%
&.hide
opacity: 0
transform: translateY(1rem)
.sniffle__notification-time .sniffle__notification-time
width: 100% width: 100%

View file

@ -7,7 +7,7 @@
display: none display: none
background-color: $bg-transparent background-color: var(--background-shade)
opacity: 0 opacity: 0
z-index: 101 z-index: 101
@ -38,12 +38,12 @@
display: flex display: flex
flex-direction: column flex-direction: column
background-color: RGB($bg-200) background-color: var(--background-400)
border-radius: $rad border-radius: var(--rad)
overflow: hidden overflow: hidden
z-index: +2 z-index: +2
transition: transform 0.2s $animation-smooth transition: transform 0.2s var(--animation-bounce)
.pop-up-header .pop-up-header
margin: 0 0 0.5rem 0 margin: 0 0 0.5rem 0
@ -73,7 +73,7 @@
font-weight: 700 font-weight: 700
text-align: left text-align: left
color: RGB($fg-white) color: var(--foreground-white)
p p
margin: 0 margin: 0
@ -84,7 +84,7 @@
font-weight: 400 font-weight: 400
text-align: left text-align: left
color: RGB($fg-white) color: var(--foreground-white)
svg svg
width: 1rem width: 1rem
@ -94,7 +94,7 @@
vertical-align: middle vertical-align: middle
a, .link a, .link
color: RGB($primary) color: var(--primary)
cursor: pointer cursor: pointer
text-decoration: none text-decoration: none
@ -127,15 +127,13 @@
justify-content: flex-end justify-content: flex-end
gap: 0.5rem gap: 0.5rem
// background-color: RGB($bg-100)
&.active &.active
opacity: 1 opacity: 1
.pop-up-wrapper .pop-up-wrapper
transform: translate(-50%, 50%) scale(1) transform: translate(-50%, 50%) scale(1)
@media (max-width: $breakpoint) @media (max-width: 800px)
.pop-up .pop-up
.pop-up-wrapper .pop-up-wrapper
max-width: calc(100% - 0.75rem) max-width: calc(100% - 0.75rem)

View file

@ -9,10 +9,10 @@
justify-content: center justify-content: center
gap: 1rem gap: 1rem
background-color: RGB($bg-400) background-color: var(--background-400)
color: RGB($fg-white) color: var(--foreground-white)
border: 2px solid RGB($bg-200) border: 2px solid var(--background-200)
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
h2 h2
margin: 0 margin: 0

View file

@ -11,10 +11,10 @@
font-weight: 500 font-weight: 500
text-decoration: none text-decoration: none
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
border: none border: none
background-color: RGBA($primary, 0.1) background-color: var(--primary-transparent)
color: RGB($primary) color: var(--primary)
cursor: pointer cursor: pointer
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out
@ -23,4 +23,4 @@
font-size: 1.15rem font-size: 1.15rem
&:hover &:hover
background-color: RGBA($primary, 0.2) background-color: var(--primary-transparent)

View file

@ -9,11 +9,11 @@
height: 100vh height: 100vh
background-color: transparent background-color: transparent
color: RGB($fg-white) color: var(--foreground-white)
overflow: hidden overflow: hidden
z-index: 68 z-index: 68
transition: background-color 0.25s cubic-bezier(0.76, 0, 0.17, 1) transition: background-color 0.25s var(--animation-smooth)
h3 h3
margin: 0 margin: 0
@ -66,11 +66,11 @@
flex-direction: column flex-direction: column
gap: 1rem gap: 1rem
background-color: RGB($bg-200) background-color: var(--background-200)
z-index: +2 z-index: +2
transition: left 0.25s cubic-bezier(0.76, 0, 0.17, 1), bottom 0.25s cubic-bezier(0.76, 0, 0.17, 1) transition: left 0.25s var(--animation-smooth), bottom 0.25s var(--animation-smooth)
#dragIndicator #dragIndicator
display: none display: none
@ -94,21 +94,21 @@
left: 50% left: 50%
transform: translate(-50%, -50%) transform: translate(-50%, -50%)
background-color: RGB($bg-400) background-color: var(--background-400)
border-radius: $rad-inner border-radius: calc(var(--rad) / 2)
transition: width 0.25s $animation-bounce transition: width 0.25s var(--animation-smooth)
&.dragging #dragIndicator::after &.dragging #dragIndicator::after
width: 9rem width: 9rem
background-color: RGB($primary) background-color: var(--primary)
.upload-jobs .upload-jobs
display: flex display: flex
flex-direction: column flex-direction: column
gap: 0.5rem gap: 0.5rem
border-radius: $rad border-radius: var(--rad)
overflow-y: auto overflow-y: auto
@ -123,8 +123,8 @@
align-items: center align-items: center
gap: 0.5rem gap: 0.5rem
background-color: RGB($bg-200) background-color: var(--background-200)
border-radius: $rad border-radius: var(--rad)
overflow: hidden overflow: hidden
@ -146,7 +146,7 @@
width: 100% width: 100%
height: 100% height: 100%
background-image: linear-gradient(to right, RGB($bg-100), transparent) background-image: linear-gradient(to right, var(--background-100), transparent)
.job__status .job__status
margin: 0 margin: 0
@ -159,51 +159,51 @@
font-size: 1rem font-size: 1rem
font-weight: 600 font-weight: 600
color: RGB($fg-white) color: var(--foreground-white)
z-index: +3 z-index: +3
transition: color 0.25s cubic-bezier(0.76, 0, 0.17, 1) transition: color 0.25s var(--animation-smooth)
.progress .progress
width: 100% width: 100%
height: $rad-inner height: calc(var(--rad) / 2)
position: absolute position: absolute
bottom: 0 bottom: 0
left: -100% left: -100%
background-color: RGB($primary) background-color: var(--primary)
animation: uploadingLoop 1s cubic-bezier(0.76, 0, 0.17, 1) infinite animation: uploadingLoop 1s var(--animation-smooth) infinite
z-index: +5 z-index: +5
transition: left 1s cubic-bezier(0.76, 0, 0.17, 1) transition: left 1s var(--animation-smooth)
&.critical &.critical
.job__status, .progress .job__status, .progress
color: RGB($critical) color: var(--critical)
&.success &.success
.job__status .job__status
color: RGB($success) color: var(--success)
.progress .progress
height: 0 height: 0
animation: none animation: none
&.warning &.warning
.job__status, .progress .job__status, .progress
color: RGB($warning) color: var(--warning)
&.critical, &.success, &.warning &.critical, &.success, &.warning
.progress .progress
height: 0 height: 0
&.open &.open
background-color: $bg-transparent background-color: var(--background-shade)
.container .container
left: 0 left: 0
@media (max-width: $breakpoint) @media (max-width: 800px)
.upload-panel .upload-panel
width: 100% width: 100%
height: calc(100vh - 3.5rem) height: calc(100vh - 3.5rem)
@ -219,7 +219,7 @@
left: 0 left: 0
bottom: -100vh bottom: -100vh
border-radius: $rad $rad 0 0 border-radius: var(--rad) var(--rad) 0 0
#dragIndicator #dragIndicator
display: block display: block

View file

@ -1,6 +1,7 @@
// Default theme for OnlyLegs by FluffyBean // Default theme for OnlyLegs by FluffyBean
// Mockup link: https://www.figma.com/file/IMZT5kZr3sAngrSHSGu5di/OnlyLegs?node-id=0%3A1 // Mockup link: https://www.figma.com/file/IMZT5kZr3sAngrSHSGu5di/OnlyLegs?node-id=0%3A1
@import "variables" @import "variables"
@import "animations" @import "animations"
@ -15,7 +16,6 @@
@import "components/gallery" @import "components/gallery"
@import "components/buttons/top-of-page" @import "components/buttons/top-of-page"
@import "components/buttons/info-button"
@import "components/buttons/pill" @import "components/buttons/pill"
@import "components/buttons/block" @import "components/buttons/block"
@ -24,17 +24,17 @@
* *
box-sizing: border-box box-sizing: border-box
scrollbar-color: RGB($primary) transparent scrollbar-color: var(--primary) transparent
font-family: $font font-family: var(--font-family)
::-webkit-scrollbar ::-webkit-scrollbar
width: 0.5rem width: 0.5rem
::-webkit-scrollbar-track ::-webkit-scrollbar-track
background: RGB($bg-200) background: var(--background-200)
::-webkit-scrollbar-thumb ::-webkit-scrollbar-thumb
background: RGB($primary) background: var(--primary)
::-webkit-scrollbar-thumb:hover ::-webkit-scrollbar-thumb:hover
background: RGB($fg-white) background: var(--foreground-white)
html html
margin: 0 margin: 0
@ -51,25 +51,35 @@ body
display: grid display: grid
grid-template-rows: auto 1fr auto grid-template-rows: auto 1fr auto
background-color: RGB($background) font-family: var(--font-family)
color: RGB($foreground)
background-color: var(--background-100)
color: var(--foreground-white)
overflow-x: hidden overflow-x: hidden
@media (max-width: $breakpoint) @media (max-width: 800px)
body body
padding: 0 0 3.5rem 0 padding: 0 0 3.5rem 0
main main
margin: 0 0.5rem 0.5rem 0
padding: 0.5rem
display: flex display: flex
flex-direction: column flex-direction: column
position: relative position: relative
background: RGBA($white, 1) background: var(--background-800)
color: RGB($fg-black) color: var(--foreground-black)
border-top-left-radius: $rad border-radius: var(--rad)
overflow: hidden overflow: hidden
@media (max-width: $breakpoint) @media (max-width: 800px)
main main
border-top-left-radius: 0 margin: 0 0.5rem
// This is very broken, as it breaks when you open any context menu/popup
// I need to fix this at some point as it looks really nice
//header
// position: sticky
// top: 0
.error-page .error-page
min-height: 100% min-height: 100%
@ -86,7 +96,7 @@ main
font-weight: 900 font-weight: 900
text-align: center text-align: center
color: $primary color: var(--primary)
p p
margin: 0 2rem margin: 0 2rem
@ -95,7 +105,7 @@ main
font-size: 1.25rem font-size: 1.25rem
font-weight: 400 font-weight: 400
text-align: center text-align: center
@media (max-width: $breakpoint) @media (max-width: 800px)
.error-page .error-page
h1 h1
font-size: 4.5rem font-size: 4.5rem

View file

@ -1,80 +1,56 @@
$bg-transparent: rgba(var(--bg-dim), 0.8)
$bg-dim: var(--bg-dim)
$bg-bright: var(--bg-bright)
$bg-100: var(--bg-100)
$bg-200: var(--bg-200)
$bg-300: var(--bg-300)
$bg-400: var(--bg-400)
$bg-500: var(--bg-500)
$bg-600: var(--bg-600)
$fg-dim: var(--fg-dim)
$fg-white: var(--fg-white)
$fg-black: var(--fg-black)
$black: var(--black)
$white: var(--white)
$red: var(--red)
$orange: var(--orange)
$yellow: var(--yellow)
$green: var(--green)
$blue: var(--blue)
$purple: var(--purple)
$primary: var(--primary)
$warning: var(--warning)
$critical: var(--critical)
$success: var(--success)
$info: var(--info)
$rad: var(--rad)
$rad-inner: var(--rad-inner)
$animation-smooth: var(--animation-smooth)
$animation-bounce: var(--animation-bounce)
$font: 'Rubik', sans-serif
$breakpoint: 800px
// New variables, Slowly moving over to them because I suck at planning ahead and coding reeee
$background: var(--bg-100)
$foreground: var(--fg-white)
\:root \:root
--bg-dim: 16, 16, 16 --background-hsl-hue: 69
--bg-bright: 232, 227, 227 --background-hsl-saturation: 25%
--bg-100: 21, 21, 21
--bg-200: #{red(adjust-color(rgb(21, 21, 21), $lightness: 2%)), green(adjust-color(rgb(21, 21, 21), $lightness: 2%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 2%))}
--bg-300: #{red(adjust-color(rgb(21, 21, 21), $lightness: 4%)), green(adjust-color(rgb(21, 21, 21), $lightness: 4%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 4%))}
--bg-400: #{red(adjust-color(rgb(21, 21, 21), $lightness: 6%)), green(adjust-color(rgb(21, 21, 21), $lightness: 6%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 6%))}
--bg-500: #{red(adjust-color(rgb(21, 21, 21), $lightness: 8%)), green(adjust-color(rgb(21, 21, 21), $lightness: 8%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 8%))}
--bg-600: #{red(adjust-color(rgb(21, 21, 21), $lightness: 10%)), green(adjust-color(rgb(21, 21, 21), $lightness: 10%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 10%))}
--fg-dim: 102, 102, 102 --background-100: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%)
--fg-white: 232, 227, 227 --background-200: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 8%)
--fg-black: 16, 16, 16 --background-300: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 10%)
--background-400: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 12%)
--background-500: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 14%)
--background-600: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 16%)
--background-700: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 18%)
--background-800: rgb(232, 227, 227)
--black: 21, 21, 21 --background-shade: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%, 0.5)
--white: 232, 227, 227
--red: 182, 100, 103 --foreground-white: rgb(232, 227, 227)
--orange: 217, 140, 95 --foreground-gray: rgb(102, 102, 102)
--yellow: 217, 188, 140 --foreground-black: rgb(16, 16, 16)
--green: 140, 151, 125
--blue: 141, 163, 185 --black: rgb(20, 20, 20)
--purple: 169, 136, 176 --black-transparent: rgba(20, 20, 20, 0.2)
--white: rgb(232, 227, 227)
--white-transparent: rgba(232, 227, 227, 0.2)
--red: rgb(182, 100, 103)
--red-transparent: rgba(182, 100, 103, 0.1)
--orange: rgb(217, 140, 95)
--orange-transparent: rgba(217, 140, 95, 0.1)
--yellow: rgb(198, 185, 166)
--yellow-transparent: rgba(198, 185, 166, 0.1)
--green: rgb(140, 151, 125)
--green-transparent: rgba(140, 151, 125, 0.1)
--blue: rgb(141, 163, 185)
--blue-transparent: rgba(141, 163, 185, 0.1)
--purple: rgb(169, 136, 176)
--purple-transparent: rgba(169, 136, 176, 0.1)
--primary: rgb(183, 169, 151)
--primary-transparent: rgba(183, 169, 151, 0.1)
--primary: var(--green) // 183, 169, 151
--warning: var(--orange) --warning: var(--orange)
--critical: var(--red) --warning-transparent: var(--orange-transparent)
--danger: var(--red)
--danger-transparent: var(--red-transparent)
--success: var(--green) --success: var(--green)
--success-transparent: var(--green-transparent)
--info: var(--blue) --info: var(--blue)
--info-transparent: var(--blue-transparent)
--rad: 0.5rem --rad: 0.4rem
--rad-inner: calc(var(--rad) / 2)
--animation-smooth: cubic-bezier(0.76, 0, 0.17, 1) --animation-smooth: cubic-bezier(0.76, 0, 0.17, 1)
--animation-bounce: cubic-bezier(.68,-0.55,.27,1.55) --animation-bounce: cubic-bezier(.68,-0.55,.27,1.55)
--breakpoint: 800px --breakpoint: 800px
--font-family: 'Switzer', sans-serif

View file

@ -17,9 +17,7 @@
<meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}"> <meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}">
<!-- Fonts --> <!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link href="https://api.fontshare.com/v2/css?f[]=switzer@101,600,701,800,501,601,900,100,700,901,400,201,401,200,300,301,801,500&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap">
<!-- phosphor icons --> <!-- phosphor icons -->
<script src="https://unpkg.com/@phosphor-icons/web"></script> <script src="https://unpkg.com/@phosphor-icons/web"></script>
@ -29,6 +27,23 @@
{% assets "scripts" %} <script type="text/javascript" src="{{ ASSET_URL }}"></script> {% endassets %} {% assets "scripts" %} <script type="text/javascript" src="{{ ASSET_URL }}"></script> {% endassets %}
{% assets "styles" %} <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> {% endassets %} {% assets "styles" %} <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> {% endassets %}
<style>
{% if config.WEBSITE_CONF.styling.force %}
:root{
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }} !important;
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}% !important;
--rad: {{ config.WEBSITE_CONF.styling.rad }} !important;
}
{% else %}
:root{
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }};
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}%;
--rad: {{ config.WEBSITE_CONF.styling.rad }};
}
{% endif %}
</style>
{% block head %}{% endblock %} {% block head %}{% endblock %}
</head> </head>
<body> <body>
@ -123,6 +138,10 @@
<script type="text/javascript"> <script type="text/javascript">
keepSquare(); keepSquare();
{% for message in get_flashed_messages() %}
addNotification('{{ message[0] }}', {{ message[1] }});
{% endfor %}
const times = document.querySelectorAll('.time'); const times = document.querySelectorAll('.time');
for (let i = 0; i < times.length; i++) { for (let i = 0; i < times.length; i++) {
// Remove milliseconds // Remove milliseconds
@ -184,10 +203,6 @@
} }
} }
} }
{% for message in get_flashed_messages() %}
addNotification('{{ message[0] }}', {{ message[1] }});
{% endfor %}
</script> </script>
{% block script %}{% endblock %} {% block script %}{% endblock %}

View file

@ -20,40 +20,9 @@
<style> <style>
{% if images %} {% if images %}
:root { --bg-100: {{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }} } :root {
--background-hsl-hue: {{ images.0.colours.0 | hsl_hue }};
body { --background-hsl-saturation: {{ images.0.colours.0 | hsl_saturation }}%;
background: rgb{{ images.0.colours.0 }} !important;
color: {{ text_colour }} !important;
}
main {
background: rgba(var(--white), 0.6) !important;
}
.navigation-item.selected { color: {{ text_colour }} !important; }
.banner .banner-content .banner-header,
.banner .banner-content .banner-info,
.banner .banner-content .banner-subtitle {
color: {{ text_colour }} !important;
}
.banner-content .link {
background-color: {{ text_colour }} !important;
color: rgb{{ images.0.colours.0 }} !important;
}
.banner-content .link:hover {
background-color: rgb{{ images.0.colours.0 }} !important;
color: {{ text_colour }} !important;
}
.banner-filter {
background: linear-gradient(90deg, rgb{{ images.0.colours.0 }}, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.3)) !important;
}
@media (max-width: 800px) {
.banner-filter {
background: linear-gradient(180deg, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.4), rgba({{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }}, 0.3)) !important;
}
} }
{% endif %} {% endif %}
</style> </style>

View file

@ -18,6 +18,10 @@
</script> </script>
<style> <style>
:root {
--background-hsl-hue: {{ image.colours.2 | hsl_hue }};
--background-hsl-saturation: {{ image.colours.2 | hsl_saturation }}%;
}
.background::after { .background::after {
background-image: linear-gradient(to top, rgba({{ image.colours.0.0 }}, {{ image.colours.0.1 }}, {{ image.colours.0.2 }}, 0.8), background-image: linear-gradient(to top, rgba({{ image.colours.0.0 }}, {{ image.colours.0.1 }}, {{ image.colours.0.2 }}, 0.8),
rgba({{ image.colours.1.0 }}, {{ image.colours.1.1 }}, {{ image.colours.1.2 }}, 0.2)); rgba({{ image.colours.1.0 }}, {{ image.colours.1.1 }}, {{ image.colours.1.2 }}, 0.2));
@ -30,15 +34,23 @@
<div class="banner-content"> <div class="banner-content">
<h1 class="banner-header">{{ config.WEBSITE_CONF.name }}</h1> <h1 class="banner-header">{{ config.WEBSITE_CONF.name }}</h1>
<div class="pill-row"> <div class="pill-row">
{% if next_url %}<div><a class="pill-item" href="{{ next_url }}"><i class="ph ph-arrow-left"></i></a></div>{% endif %}
<div> <div>
<button class="pill-item" onclick="imageFullscreen()"><i class="ph ph-info"></i></button> <a {% if next_url %}class="pill-item" href="{{ next_url }}"{% else %}class="pill-item disabled"{% endif %}>
<i class="ph ph-arrow-left"></i>
</a>
</div>
<div>
<button class="pill-item" onclick="imageFullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button> <button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
{% if image.author.id == current_user.id %} {% if image.author.id == current_user.id %}
<button class="pill-item pill__critical" onclick="imageShowOptionsPopup(this)"><i class="ph-fill ph-dots-three-outline-vertical"></i></button> <button class="pill-item" onclick="imageShowOptionsPopup(this)"><i class="ph ph-list"></i></button>
{% endif %} {% endif %}
</div> </div>
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %} <div>
<a {% if prev_url %}class="pill-item" href="{{ prev_url }}"{% else %}class="pill-item disabled"{% endif %}>
<i class="ph ph-arrow-right"></i>
</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -29,7 +29,7 @@
{% block header %} {% block header %}
<div class="banner"> <div class="banner">
{% if user.banner %} {% if user.banner %}
<img src="{{ url_for('static', filename='icon.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/> <img src="{{ url_for('api.media', path='banner/' + user.banner) }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/>
{% else %} {% else %}
<img src="{{ url_for('static', filename='banner.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/> <img src="{{ url_for('static', filename='banner.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/>
{% endif %} {% endif %}

View file

@ -16,50 +16,86 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="info-tab" id="profileSettings" style="margin: 0.5rem 0.5rem 0 0.5rem"> <details open>
<div class="info-header"> <summary>
<i class="ph ph-info"></i> <i class="ph ph-info"></i><h2>Profile</h2><span style="width: 100%"></span>
<h2>Profile Settings</h2> <i class="ph ph-caret-down collapse-indicator"></i>
<button class="collapse-indicator"><i class="ph ph-caret-down"></i></button> </summary>
</div>
<div class="info-table">
<form method="POST" action="{{ url_for('api.account_picture', user_id=current_user.id) }}" enctype="multipart/form-data">
<h3>Profile Picture</h3>
<input type="file" name="file" tab-index="-1"/>
<input type="submit" value="Upload" class="btn-block">
</form>
<form method="POST" action="{{ url_for('api.account_username', user_id=current_user.id) }}" enctype="multipart/form-data">
<h3>Username</h3>
<input type="text" name="name" class="input-block" value="{{ current_user.username }}" />
<input type="submit" value="Upload" class="btn-block"/>
</form>
</div>
</div>
<div class="info-tab" id="profileSettings" style="margin: 0.5rem 0.5rem 0 0.5rem"> <form method="POST" action="{{ url_for('settings.account_picture') }}" enctype="multipart/form-data">
<div class="info-header"> <h3>Profile Picture</h3>
<i class="ph ph-info"></i> <input type="file" name="file" tab-index="-1"/>
<h2>Account Settings</h2> <button type="submit" class="btn-block">Change Profile Picture</button>
<button class="collapse-indicator"><i class="ph ph-caret-down"></i></button> </form>
</div> <form method="POST" action="{{ url_for('settings.account_banner') }}" enctype="multipart/form-data">
<div class="info-table"> <h3>Profile Banner</h3>
<form method="POST" action="" enctype="multipart/form-data"> <input type="file" name="file" tab-index="-1"/>
<h3>Email</h3> <button type="submit" class="btn-block">Change Profile Banner</button>
<input type="text" name="email" class="input-block" value="{{ current_user.email }}" /> </form>
<input type="submit" value="Upload" class="btn-block"/> <form method="POST" action="{{ url_for('settings.account_username') }}" enctype="multipart/form-data">
</form> <h3>Username</h3>
</div> <input type="text" name="name" class="input-block" value="{{ current_user.username }}" />
</div> <button type="submit" class="btn-block">Change Username</button>
</form>
</details>
<details open>
<summary>
<i class="ph ph-info"></i><h2>Account</h2><span style="width: 100%"></span>
<i class="ph ph-caret-down collapse-indicator"></i>
</summary>
<form method="POST" action="{{ url_for('settings.account_email') }}" enctype="multipart/form-data">
<h3>Email</h3>
<input type="text" name="email" class="input-block" value="{{ current_user.email }}" />
<input type="password" name="current" class="input-block" placeholder="Current Password" />
<button type="submit" class="btn-block">Change Email</button>
</form>
<form method="POST" action="{{ url_for('settings.account_password') }}" enctype="multipart/form-data">
<h3>Password</h3>
<input type="password" name="current" class="input-block" placeholder="Current Password" />
<input type="password" name="password" class="input-block" placeholder="New Password" />
<input type="password" name="confirm" class="input-block" placeholder="Confirm Password" />
<button type="submit" class="btn-block">Change Password</button>
</form>
</details>
<details open>
<summary>
<i class="ph ph-info"></i><h2>Server</h2><span style="width: 100%"></span>
<i class="ph ph-caret-down collapse-indicator"></i>
</summary>
<form method="POST" action="{{ url_for('settings.website_style') }}" enctype="multipart/form-data">
<h3>Style</h3>
<input type="range" name="hue" class="input-block" placeholder="Website Hue" value="{{ config.WEBSITE_CONF.styling.hue }}" min="0" max="360" />
<input type="range" name="saturation" class="input-block" placeholder="Strength of Color" value="{{ config.WEBSITE_CONF.styling.saturation }}" min="0" max="100" />
<input type="text" name="rad" class="input-block" placeholder="Roundy roundy" value="{{ config.WEBSITE_CONF.styling.rad }}" />
<input type="checkbox" name="force" class="input-block" placeholder="Force styling?" {% if config.WEBSITE_CONF.styling.force %}checked{% endif %} />
<button type="submit" class="btn-block">Update style</button>
</form>
</details>
<footer>
<p>Built on coffee and love, by Fluffy</p>
</footer>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script type="text/javascript"> <script>
let infoTab = document.querySelectorAll('.info-tab'); let hue = document.querySelector('input[name=hue]');
let saturation = document.querySelector('input[name=saturation]');
let rad = document.querySelector('input[name=rad]');
for (let i = 0; i < infoTab.length; i++) { hue.addEventListener('input', () => {
infoTab[i].querySelector('.collapse-indicator').addEventListener('click', function() { document.documentElement.style.setProperty('--background-hsl-hue', hue.value, 'important');
infoTab[i].classList.toggle('collapsed'); });
}); saturation.addEventListener('input', () => {
} document.documentElement.style.setProperty('--background-hsl-saturation', saturation.value + '%', 'important');
});
rad.addEventListener('input', () => {
document.documentElement.style.setProperty('--rad', rad.value, 'important');
});
</script> </script>
{% endblock %} {% endblock %}

View file

@ -58,7 +58,7 @@ class Colour:
s = 0.0 s = 0.0
else: else:
d = high - low d = high - low
s = d / (2 - high - low) if l > 0.5 else d / (high + low) s = d / (2 - high - low) if low > 0.5 else d / (high + low)
h = { h = {
r: (g - b) / d + (6 if g < b else 0), r: (g - b) / d + (6 if g < b else 0),
g: (b - r) / d + 2, g: (b - r) / d + 2,

133
onlylegs/utils/startup.py Normal file
View file

@ -0,0 +1,133 @@
"""
OnlyLegs - Setup
Runs when the app detects that there is no user directory
"""
import os
import re
import platformdirs
import yaml
APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs")
REQUIRED_DIRS = {
"root": APPLICATION_ROOT,
"instance": os.path.join(APPLICATION_ROOT, "instance"),
"media": os.path.join(APPLICATION_ROOT, "media"),
"uploads": os.path.join(APPLICATION_ROOT, "media", "uploads"),
"cache": os.path.join(APPLICATION_ROOT, "media", "cache"),
"pfp": os.path.join(APPLICATION_ROOT, "media", "pfp"),
"banner": os.path.join(APPLICATION_ROOT, "media", "banner"),
}
EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
USERNAME_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
config = {
# Version of the config file
"version": "0.1.7",
# Not really used much, but good to have for future use
"admin": {
"username": "admin",
"email": "admin@example.com",
},
"upload": {
"allowed-extensions": {
"jpg": "jpeg",
"jpeg": "jpeg",
"png": "png",
"webp": "webp",
},
# Max size in MB
"max-size": 69,
# Max images to load per page
"max-load": 50,
},
"website": {
# Website name and motto
# Also CSS styling, hue is the color offset for hsl
"name": "OnlyLegs",
"motto": "A gallery built for fast and simple image management!",
"styling": {
"force": False,
"hue": "69",
"saturation": "25%",
"rad": "0.4rem",
},
},
}
def check_dirs():
for directory in REQUIRED_DIRS.values():
if not os.path.exists(directory):
os.makedirs(directory)
print("Created directory at:", directory)
print("User directory already exists at:", directory)
def check_env():
if os.path.exists(os.path.join(APPLICATION_ROOT, ".env")):
print("Environment file already exists at:", APPLICATION_ROOT)
return
env_conf = {
"FLASK_SECRET": os.urandom(32).hex(),
}
with open(
os.path.join(APPLICATION_ROOT, ".env"), encoding="utf-8", mode="w+"
) as file:
for key, value in env_conf.items():
file.write(key + "=" + value + "\n")
print(
"####################################################",
"# A NEW KEY WAS GENERATED FOR YOU! PLEASE NOTE #",
"# DOWN THE FLASK_SECRET KEY LOCATED IN YOUR #",
"# ~/.config/onlylegs/.env FOLDER! LOOSING THIS KEY #",
"# WILL RESULT IN YOU BEING UNABLE TO LOG IN! #",
"####################################################",
sep="\n",
)
def check_conf():
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
if os.path.exists(config_file):
print("Config file already exists at:", APPLICATION_ROOT)
return
cant_continue = True
username = "admin"
email = "admin@example.com"
print("No config file found, please enter the following information:")
while cant_continue:
username = input("Admin username: ").strip()
email = input("Admin email: ").strip()
if not username or not USERNAME_REGEX.match(username):
print("Username is invalid!")
continue
if not email or not EMAIL_REGEX.match(email):
print("Email is invalid!")
continue
# Check if user is happy with the values
is_correct = input("Is this correct? (Y/n): ").lower().strip()
if is_correct == "y" or not is_correct:
cant_continue = False
config["admin"]["username"] = username
config["admin"]["email"] = email
with open(config_file, encoding="utf-8", mode="w+") as file:
yaml.dump(config, file, default_flow_style=False)
print(
"####################################################",
"# A NEW CONFIG HAS BEEN GENERATED AT: #",
"# ~/.config/onlylegs/conf.yml #",
"####################################################",
sep="\n",
)

View file

@ -1,17 +1,191 @@
""" """
OnlyLegs - Settings page OnlyLegs - Settings page
""" """
from flask import Blueprint, render_template import os
from flask_login import login_required import pathlib
import re
import logging
import yaml
from colorthief import ColorThief
from flask import (
Blueprint,
request,
current_app,
render_template,
flash,
redirect,
url_for,
)
from flask_login import login_required, current_user
from werkzeug.security import check_password_hash, generate_password_hash
from onlylegs.extensions import db
from onlylegs.models import Users
from onlylegs.config import APPLICATION_ROOT
blueprint = Blueprint("settings", __name__, url_prefix="/settings") blueprint = Blueprint("settings", __name__, url_prefix="/settings")
@blueprint.route("/") @blueprint.route("/", methods=["GET"])
@login_required @login_required
def general(): def general():
""" """
General settings page General settings page
""" """
return render_template("settings.html") return render_template("settings.html")
@blueprint.route("/account/pfp", methods=["POST"])
@login_required
def account_picture():
user_record = Users.query.filter_by(id=current_user.id).first()
uploaded_file = request.files.get("file", None)
if not uploaded_file:
return "No file uploaded!", 400
image_mime = pathlib.Path(uploaded_file.filename).suffix.replace(".", "").lower()
image_name = str(user_record.id) + "_pfp." + image_mime
image_path = os.path.join(current_app.config["PFP_FOLDER"], image_name)
if image_mime not in current_app.config["ALLOWED_EXTENSIONS"].keys():
logging.info("File extension not allowed: %s", image_mime)
return "File extension not allowed", 403
if user_record.picture:
os.remove(os.path.join(current_app.config["PFP_FOLDER"], user_record.picture))
cache_name = user_record.picture.rsplit(".")[0]
for file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
cache_name + "*"
):
os.remove(file)
uploaded_file.save(image_path)
image_colours = ColorThief(image_path).get_color()
user_record.colour = image_colours
user_record.picture = image_name
db.session.commit()
return "File uploaded", 200
@blueprint.route("/account/banner", methods=["POST"])
@login_required
def account_banner():
user_record = Users.query.filter_by(id=current_user.id).first()
uploaded_file = request.files.get("file", None)
if not uploaded_file:
return "No file uploaded!", 400
image_mime = pathlib.Path(uploaded_file.filename).suffix.replace(".", "").lower()
image_name = str(user_record.id) + "_banner." + image_mime
image_path = os.path.join(current_app.config["BANNER_FOLDER"], image_name)
if image_mime not in current_app.config["ALLOWED_EXTENSIONS"].keys():
logging.info("File extension not allowed: %s", image_mime)
return "File extension not allowed", 403
if user_record.banner:
os.remove(os.path.join(current_app.config["BANNER_FOLDER"], user_record.banner))
cache_name = user_record.banner.rsplit(".")[0]
for file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
cache_name + "*"
):
os.remove(file)
uploaded_file.save(image_path)
user_record.banner = image_name
db.session.commit()
return "File uploaded", 200
@blueprint.route("/account/username", methods=["POST"])
@login_required
def account_username():
user_record = Users.query.filter_by(id=current_user.id).first()
new_username = request.form.get("username", "").strip()
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
if not new_username or not username_regex.match(new_username):
return "Username is invalid", 400
user_record.username = new_username
db.session.commit()
return "Username changed", 200
@blueprint.route("/account/email", methods=["POST"])
@login_required
def account_email():
user_record = Users.query.filter_by(id=current_user.id).first()
current_password = request.form.get("current", "").strip()
new_email = request.form.get("email", "").strip()
email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
if not current_password or not new_email:
return "Fill in all the fields!", 400
if not email_regex.match(new_email):
return "Email is invalid!", 400
if not check_password_hash(user_record.password, current_password):
return "Incorrect password!", 400
user_record.email = new_email
db.session.commit()
return "Email changed", 200
@blueprint.route("/account/password", methods=["POST"])
@login_required
def account_password():
user_record = Users.query.filter_by(id=current_user.id).first()
current_password = request.form.get("current", "").strip()
new_password = request.form.get("password", "").strip()
new_confirm = request.form.get("confirm", "").strip()
if not current_password or not new_password or not new_confirm:
return "Fill in all the fields!", 400
if new_password != new_confirm:
return "Passwords do not match!", 400
if not check_password_hash(user_record.password, current_password):
return "Incorrect password!", 400
user_record.password = generate_password_hash(new_password, method="scrypt")
db.session.commit()
flash(["Password changed! You must login now", 0])
return redirect(url_for("auth.logout"))
@blueprint.route("/website/style", methods=["POST"])
@login_required
def website_style():
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
website_hue = request.form.get("hue", 69, type=int)
website_saturation = request.form.get("saturation", 25, type=int)
website_rad = request.form.get("rad", "0.4rem").strip()
website_force_styling = request.form.get("force", False)
config = None
with open(config_file, "r") as file:
config = yaml.safe_load(file)
current_app.config["WEBSITE_CONF"]["styling"]["hue"] = website_hue
current_app.config["WEBSITE_CONF"]["styling"]["saturation"] = website_saturation
current_app.config["WEBSITE_CONF"]["styling"]["rad"] = website_rad
current_app.config["WEBSITE_CONF"]["styling"]["force"] = website_force_styling
config["website"]["styling"]["hue"] = website_hue
config["website"]["styling"]["saturation"] = website_saturation
config["website"]["styling"]["rad"] = website_rad
config["website"]["styling"]["force"] = website_force_styling
with open(config_file, "w") as file:
yaml.dump(config, file)
return "Website style changed", 200

662
poetry.lock generated
View file

@ -1,15 +1,15 @@
# This file is automatically @generated by Poetry and should not be changed by hand. # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]] [[package]]
name = "alembic" name = "alembic"
version = "1.11.2" version = "1.12.0"
description = "A database migration tool for SQLAlchemy." description = "A database migration tool for SQLAlchemy."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "alembic-1.11.2-py3-none-any.whl", hash = "sha256:7981ab0c4fad4fe1be0cf183aae17689fe394ff874fd2464adb774396faf0796"}, {file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"},
{file = "alembic-1.11.2.tar.gz", hash = "sha256:678f662130dc540dac12de0ea73de9f89caea9dbea138f60ef6263149bf84657"}, {file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"},
] ]
[package.dependencies] [package.dependencies]
@ -24,14 +24,14 @@ tz = ["python-dateutil"]
[[package]] [[package]]
name = "astroid" name = "astroid"
version = "2.15.6" version = "2.15.8"
description = "An abstract syntax tree for Python with inference support." description = "An abstract syntax tree for Python with inference support."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
{file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"}, {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
{file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"}, {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
] ]
[package.dependencies] [package.dependencies]
@ -44,34 +44,34 @@ wrapt = [
[[package]] [[package]]
name = "black" name = "black"
version = "23.7.0" version = "23.9.1"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
] ]
[package.dependencies] [package.dependencies]
@ -81,7 +81,7 @@ packaging = ">=22.0"
pathspec = ">=0.9.0" pathspec = ">=0.9.0"
platformdirs = ">=2" platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras] [package.extras]
colorama = ["colorama (>=0.4.3)"] colorama = ["colorama (>=0.4.3)"]
@ -103,96 +103,137 @@ files = [
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "1.0.9" version = "1.1.0"
description = "Python bindings for the Brotli compression library" description = "Python bindings for the Brotli compression library"
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"},
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"},
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"}, {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"},
{file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"}, {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"},
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"}, {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"},
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"}, {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"},
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"},
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"},
{file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"}, {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"},
{file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"}, {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"},
{file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"}, {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"},
{file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"}, {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"},
{file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"},
{file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"},
{file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"},
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"},
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"},
{file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"},
{file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"},
{file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"},
{file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"},
{file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"},
{file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"},
{file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"},
{file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"}, {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"},
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"}, {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"},
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"}, {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"}, {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"},
{file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"},
{file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"},
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"},
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"}, {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"}, {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"}, {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"}, {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"},
{file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"},
{file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"}, {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"}, {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337"}, {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be"}, {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"},
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"},
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
] ]
[[package]]
name = "brotlicffi"
version = "1.1.0.0"
description = "Python CFFI bindings to the Brotli library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"},
{file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"},
{file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814"},
{file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820"},
{file = "brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb"},
{file = "brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613"},
{file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e4aeb0bd2540cb91b069dbdd54d458da8c4334ceaf2d25df2f4af576d6766ca"},
{file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b7b0033b0d37bb33009fb2fef73310e432e76f688af76c156b3594389d81391"},
{file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54a07bb2374a1eba8ebb52b6fafffa2afd3c4df85ddd38fcc0511f2bb387c2a8"},
{file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7901a7dc4b88f1c1475de59ae9be59799db1007b7d059817948d8e4f12e24e35"},
{file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce01c7316aebc7fce59da734286148b1d1b9455f89cf2c8a4dfce7d41db55c2d"},
{file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:246f1d1a90279bb6069de3de8d75a8856e073b8ff0b09dcca18ccc14cec85979"},
{file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc4bc5d82bc56ebd8b514fb8350cfac4627d6b0743382e46d033976a5f80fab6"},
{file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c26ecb14386a44b118ce36e546ce307f4810bc9598a6e6cb4f7fca725ae7e6"},
{file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca72968ae4eaf6470498d5c2887073f7efe3b1e7d7ec8be11a06a79cc810e990"},
{file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:add0de5b9ad9e9aa293c3aa4e9deb2b61e99ad6c1634e01d01d98c03e6a354cc"},
{file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b6068e0f3769992d6b622a1cd2e7835eae3cf8d9da123d7f51ca9c1e9c333e5"},
{file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8557a8559509b61e65083f8782329188a250102372576093c88930c875a69838"},
{file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a7ae37e5d79c5bdfb5b4b99f2715a6035e6c5bf538c3746abc8e26694f92f33"},
{file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391151ec86bb1c683835980f4816272a87eaddc46bb91cbf44f62228b84d8cca"},
{file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f3711be9290f0453de8eed5275d93d286abe26b08ab4a35d7452caa1fef532f"},
{file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a807d760763e398bbf2c6394ae9da5815901aa93ee0a37bca5efe78d4ee3171"},
{file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa8ca0623b26c94fccc3a1fdd895be1743b838f3917300506d04aa3346fd2a14"},
{file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3de0cf28a53a3238b252aca9fed1593e9d36c1d116748013339f0949bfc84112"},
{file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6be5ec0e88a4925c91f3dea2bb0013b3a2accda6f77238f76a34a1ea532a1cb0"},
{file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d9eb71bb1085d996244439154387266fd23d6ad37161f6f52f1cd41dd95a3808"},
{file = "brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13"},
]
[package.dependencies]
cffi = ">=1.0.0"
[[package]] [[package]]
name = "cachetools" name = "cachetools"
version = "5.3.1" version = "5.3.1"
@ -205,16 +246,93 @@ files = [
{file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"},
] ]
[[package]]
name = "cffi"
version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
{file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
{file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
{file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
{file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
{file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
{file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
{file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
{file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
{file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
{file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
{file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
{file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
{file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
{file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
{file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
{file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
{file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
{file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
{file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
{file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
{file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
{file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
{file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
{file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
{file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
{file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
{file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
{file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
{file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
{file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
{file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
{file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
{file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
{file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
{file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
{file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
{file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
{file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
[package.dependencies]
pycparser = "*"
[[package]] [[package]]
name = "click" name = "click"
version = "8.1.6" version = "8.1.7"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
] ]
[package.dependencies] [package.dependencies]
@ -275,14 +393,14 @@ graph = ["objgraph (>=1.7.2)"]
[[package]] [[package]]
name = "flask" name = "flask"
version = "2.3.2" version = "2.3.3"
description = "A simple framework for building complex web applications." description = "A simple framework for building complex web applications."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "Flask-2.3.2-py3-none-any.whl", hash = "sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0"}, {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"},
{file = "Flask-2.3.2.tar.gz", hash = "sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef"}, {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"},
] ]
[package.dependencies] [package.dependencies]
@ -291,7 +409,7 @@ click = ">=8.1.3"
importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
itsdangerous = ">=2.1.2" itsdangerous = ">=2.1.2"
Jinja2 = ">=3.1.2" Jinja2 = ">=3.1.2"
Werkzeug = ">=2.3.3" Werkzeug = ">=2.3.7"
[package.extras] [package.extras]
async = ["asgiref (>=3.2)"] async = ["asgiref (>=3.2)"]
@ -330,18 +448,19 @@ Flask = "*"
[[package]] [[package]]
name = "flask-compress" name = "flask-compress"
version = "1.13" version = "1.14"
description = "Compress responses in your Flask app with gzip, deflate or brotli." description = "Compress responses in your Flask app with gzip, deflate or brotli."
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "Flask-Compress-1.13.tar.gz", hash = "sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d"}, {file = "Flask-Compress-1.14.tar.gz", hash = "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee"},
{file = "Flask_Compress-1.13-py3-none-any.whl", hash = "sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b"}, {file = "Flask_Compress-1.14-py3-none-any.whl", hash = "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c"},
] ]
[package.dependencies] [package.dependencies]
brotli = "*" brotli = {version = "*", markers = "platform_python_implementation != \"PyPy\""}
brotlicffi = {version = "*", markers = "platform_python_implementation == \"PyPy\""}
flask = "*" flask = "*"
[[package]] [[package]]
@ -362,14 +481,14 @@ Werkzeug = ">=1.0.1"
[[package]] [[package]]
name = "flask-migrate" name = "flask-migrate"
version = "4.0.4" version = "4.0.5"
description = "SQLAlchemy database migrations for Flask applications using Alembic." description = "SQLAlchemy database migrations for Flask applications using Alembic."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "Flask-Migrate-4.0.4.tar.gz", hash = "sha256:73293d40b10ac17736e715b377e7b7bde474cb8105165d77474df4c3619b10b3"}, {file = "Flask-Migrate-4.0.5.tar.gz", hash = "sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1"},
{file = "Flask_Migrate-4.0.4-py3-none-any.whl", hash = "sha256:77580f27ab39bc68be4906a43c56d7674b45075bc4f883b1d0b985db5164d58f"}, {file = "Flask_Migrate-4.0.5-py3-none-any.whl", hash = "sha256:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef"},
] ]
[package.dependencies] [package.dependencies]
@ -379,19 +498,19 @@ Flask-SQLAlchemy = ">=1.0"
[[package]] [[package]]
name = "flask-sqlalchemy" name = "flask-sqlalchemy"
version = "3.0.5" version = "3.1.1"
description = "Add SQLAlchemy support to your Flask application." description = "Add SQLAlchemy support to your Flask application."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "flask_sqlalchemy-3.0.5-py3-none-any.whl", hash = "sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283"}, {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"},
{file = "flask_sqlalchemy-3.0.5.tar.gz", hash = "sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1"}, {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"},
] ]
[package.dependencies] [package.dependencies]
flask = ">=2.2.5" flask = ">=2.2.5"
sqlalchemy = ">=1.4.18" sqlalchemy = ">=2.0.16"
[[package]] [[package]]
name = "greenlet" name = "greenlet"
@ -469,18 +588,18 @@ test = ["objgraph", "psutil"]
[[package]] [[package]]
name = "gunicorn" name = "gunicorn"
version = "20.1.0" version = "21.2.0"
description = "WSGI HTTP Server for UNIX" description = "WSGI HTTP Server for UNIX"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"},
] ]
[package.dependencies] [package.dependencies]
setuptools = ">=3.0" packaging = "*"
[package.extras] [package.extras]
eventlet = ["eventlet (>=0.24.1)"] eventlet = ["eventlet (>=0.24.1)"]
@ -510,22 +629,22 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs
[[package]] [[package]]
name = "importlib-resources" name = "importlib-resources"
version = "6.0.0" version = "6.1.0"
description = "Read resources from Python packages" description = "Read resources from Python packages"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "importlib_resources-6.0.0-py3-none-any.whl", hash = "sha256:d952faee11004c045f785bb5636e8f885bed30dc3c940d5d42798a2a4541c185"}, {file = "importlib_resources-6.1.0-py3-none-any.whl", hash = "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83"},
{file = "importlib_resources-6.0.0.tar.gz", hash = "sha256:4cf94875a8368bd89531a756df9a9ebe1f150e0f885030b461237bc7f2d905f2"}, {file = "importlib_resources-6.1.0.tar.gz", hash = "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9"},
] ]
[package.dependencies] [package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras] [package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
[[package]] [[package]]
name = "isort" name = "isort"
@ -727,6 +846,22 @@ files = [
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
] ]
[[package]]
name = "material-color-utilities-python"
version = "0.1.5"
description = "Python port of material-color-utilities used for Material You colors"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "material-color-utilities-python-0.1.5.tar.gz", hash = "sha256:3c6f02e7ce70595885447bdef37cf76fd3628c7c95fa2198d8174c269c951fae"},
{file = "material_color_utilities_python-0.1.5-py2.py3-none-any.whl", hash = "sha256:48abd8695a1355ab3ad43fe314ca8664c66282a86fbf94a717571273bf422bdf"},
]
[package.dependencies]
Pillow = ">=9.2.0,<10.0.0"
regex = "*"
[[package]] [[package]]
name = "mccabe" name = "mccabe"
version = "0.7.0" version = "0.7.0"
@ -871,20 +1006,32 @@ files = [
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
[[package]]
name = "pycparser"
version = "2.21"
description = "C parser in Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
[[package]] [[package]]
name = "pylint" name = "pylint"
version = "2.17.5" version = "2.17.6"
description = "python code static checker" description = "python code static checker"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
{file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"}, {file = "pylint-2.17.6-py3-none-any.whl", hash = "sha256:18a1412e873caf8ffb56b760ce1b5643675af23e6173a247b502406b24c716af"},
{file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"}, {file = "pylint-2.17.6.tar.gz", hash = "sha256:be928cce5c76bf9acdc65ad01447a1e0b1a7bccffc609fb7fc40f2513045bd05"},
] ]
[package.dependencies] [package.dependencies]
astroid = ">=2.15.6,<=2.17.0-dev0" astroid = ">=2.15.7,<=2.17.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [ dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.2", markers = "python_version < \"3.11\""},
@ -903,14 +1050,14 @@ testutils = ["gitpython (>3)"]
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "0.21.1" version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables" description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
{file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
] ]
[package.extras] [package.extras]
@ -967,71 +1114,152 @@ files = [
] ]
[[package]] [[package]]
name = "setuptools" name = "regex"
version = "68.0.0" version = "2023.8.8"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Alternative regular expression module, to replace re."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.6"
files = [ files = [
{file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"},
{file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"},
{file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"},
{file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"},
{file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"},
{file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"},
{file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"},
{file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"},
{file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"},
{file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"},
{file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"},
{file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"},
{file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"},
{file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"},
{file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"},
{file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"},
{file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"},
{file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"},
{file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"},
{file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"},
{file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"},
{file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"},
{file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"},
{file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"},
{file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"},
{file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"},
{file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"},
{file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"},
{file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"},
{file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"},
{file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"},
{file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"},
{file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"},
{file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"},
{file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"},
{file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"},
{file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"},
{file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"},
{file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"},
{file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"},
{file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"},
{file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"},
{file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"},
{file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"},
{file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"},
{file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"},
{file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"},
{file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"},
{file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"},
{file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"},
{file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"},
{file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"},
{file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"},
{file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"},
{file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"},
{file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"},
{file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"},
{file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"},
{file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"},
{file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"},
{file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"},
{file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"},
{file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"},
] ]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "2.0.19" version = "2.0.21"
description = "Database Abstraction Library" description = "Database Abstraction Library"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9deaae357edc2091a9ed5d25e9ee8bba98bcfae454b3911adeaf159c2e9ca9e3"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bf0fd65b50a330261ec7fe3d091dfc1c577483c96a9fa1e4323e932961aa1b5"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d90ccc15ba1baa345796a8fb1965223ca7ded2d235ccbef80a47b85cea2d71a"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4e688f6784427e5f9479d1a13617f573de8f7d4aa713ba82813bcd16e259d1"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:584f66e5e1979a7a00f4935015840be627e31ca29ad13f49a6e51e97a3fb8cae"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c69ce70047b801d2aba3e5ff3cba32014558966109fecab0c39d16c18510f15"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-win32.whl", hash = "sha256:96f0463573469579d32ad0c91929548d78314ef95c210a8115346271beeeaaa2"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-win32.whl", hash = "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-win_amd64.whl", hash = "sha256:22bafb1da60c24514c141a7ff852b52f9f573fb933b1e6b5263f0daa28ce6db9"}, {file = "SQLAlchemy-2.0.21-cp310-cp310-win_amd64.whl", hash = "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6894708eeb81f6d8193e996257223b6bb4041cb05a17cd5cf373ed836ef87a2"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8f2afd1aafded7362b397581772c670f20ea84d0a780b93a1a1529da7c3d369"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15afbf5aa76f2241184c1d3b61af1a72ba31ce4161013d7cb5c4c2fca04fd6e"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc05b59142445a4efb9c1fd75c334b431d35c304b0e33f4fa0ff1ea4890f92e"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5831138f0cc06b43edf5f99541c64adf0ab0d41f9a4471fd63b54ae18399e4de"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3afa8a21a9046917b3a12ffe016ba7ebe7a55a6fc0c7d950beb303c735c3c3ad"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-win32.whl", hash = "sha256:c896d4e6ab2eba2afa1d56be3d0b936c56d4666e789bfc59d6ae76e9fcf46145"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-win32.whl", hash = "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-win_amd64.whl", hash = "sha256:024d2f67fb3ec697555e48caeb7147cfe2c08065a4f1a52d93c3d44fc8e6ad1c"}, {file = "SQLAlchemy-2.0.21-cp311-cp311-win_amd64.whl", hash = "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:89bc2b374ebee1a02fd2eae6fd0570b5ad897ee514e0f84c5c137c942772aa0c"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4d410a76c3762511ae075d50f379ae09551d92525aa5bb307f8343bf7c2c12"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f469f15068cd8351826df4080ffe4cc6377c5bf7d29b5a07b0e717dddb4c7ea2"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a95aa0672e3065d43c8aa80080cdd5cc40fe92dc873749e6c1cf23914c4b83af"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cda283700c984e699e8ef0fcc5c61f00c9d14b6f65a4f2767c97242513fcdd84"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8c323813963b2503e54d0944813cd479c10c636e3ee223bcbd7bd478bf53c178"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:43699eb3f80920cc39a380c159ae21c8a8924fe071bccb68fc509e099420b148"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:419b1276b55925b5ac9b4c7044e999f1787c69761a3c9756dec6e5c225ceca01"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win32.whl", hash = "sha256:61ada5831db36d897e28eb95f0f81814525e0d7927fb51145526c4e63174920b"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-win32.whl", hash = "sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win_amd64.whl", hash = "sha256:57d100a421d9ab4874f51285c059003292433c648df6abe6c9c904e5bd5b0828"}, {file = "SQLAlchemy-2.0.21-cp37-cp37m-win_amd64.whl", hash = "sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:16a310f5bc75a5b2ce7cb656d0e76eb13440b8354f927ff15cbaddd2523ee2d1"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4eae01faee9f2b17f08885e3f047153ae0416648f8e8c8bd9bc677c5ce64be9"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7b5e3856cbf1876da4e9d9715546fa26b6e0ba1a682d5ed2fc3ca4c7c3ec5b"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e7b69d9ced4b53310a87117824b23c509c6fc1f692aa7272d47561347e133b6"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9eb4575bfa5afc4b066528302bf12083da3175f71b64a43a7c0badda2be365"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b19ae41ef26c01a987e49e37c77b9ad060c59f94d3b3efdfdbf4f3daaca7b5fe"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6b54d1ad7a162857bb7c8ef689049c7cd9eae2f38864fc096d62ae10bc100c7d"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5d6afc41ca0ecf373366fd8e10aee2797128d3ae45eb8467b19da4899bcd1ee0"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:014794b60d2021cc8ae0f91d4d0331fe92691ae5467a00841f7130fe877b678e"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-win32.whl", hash = "sha256:430614f18443b58ceb9dedec323ecddc0abb2b34e79d03503b5a7579cd73a531"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-win32.whl", hash = "sha256:0268256a34806e5d1c8f7ee93277d7ea8cc8ae391f487213139018b6805aeaf6"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-win_amd64.whl", hash = "sha256:eb60699de43ba1a1f77363f563bb2c652f7748127ba3a774f7cf2c7804aa0d3d"}, {file = "SQLAlchemy-2.0.21-cp38-cp38-win_amd64.whl", hash = "sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a752b7a9aceb0ba173955d4f780c64ee15a1a991f1c52d307d6215c6c73b3a4c"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7351c05db355da112e056a7b731253cbeffab9dfdb3be1e895368513c7d70106"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa51ce4aea583b0c6b426f4b0563d3535c1c75986c4373a0987d84d22376585b"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae7473a67cd82a41decfea58c0eac581209a0aa30f8bc9190926fbf628bb17f7"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851a37898a8a39783aab603c7348eb5b20d83c76a14766a43f56e6ad422d1ec8"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539010665c90e60c4a1650afe4ab49ca100c74e6aef882466f1de6471d414be7"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-win32.whl", hash = "sha256:f82c310ddf97b04e1392c33cf9a70909e0ae10a7e2ddc1d64495e3abdc5d19fb"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-win32.whl", hash = "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-win_amd64.whl", hash = "sha256:8e712cfd2e07b801bc6b60fdf64853bc2bd0af33ca8fa46166a23fe11ce0dbb0"}, {file = "SQLAlchemy-2.0.21-cp39-cp39-win_amd64.whl", hash = "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce"},
{file = "SQLAlchemy-2.0.19-py3-none-any.whl", hash = "sha256:314145c1389b021a9ad5aa3a18bac6f5d939f9087d7fc5443be28cba19d2c972"}, {file = "SQLAlchemy-2.0.21-py3-none-any.whl", hash = "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce"},
{file = "SQLAlchemy-2.0.19.tar.gz", hash = "sha256:77a14fa20264af73ddcdb1e2b9c5a829b8cc6b8304d0f093271980e36c200a3f"}, {file = "SQLAlchemy-2.0.21.tar.gz", hash = "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258"},
] ]
[package.dependencies] [package.dependencies]
@ -1039,7 +1267,7 @@ greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or
typing-extensions = ">=4.2.0" typing-extensions = ">=4.2.0"
[package.extras] [package.extras]
aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"] asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
@ -1088,14 +1316,14 @@ files = [
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.7.1" version = "4.8.0"
description = "Backported and Experimental Type Hints for Python 3.7+" description = "Backported and Experimental Type Hints for Python 3.8+"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
{file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
] ]
[[package]] [[package]]
@ -1112,14 +1340,14 @@ files = [
[[package]] [[package]]
name = "werkzeug" name = "werkzeug"
version = "2.3.6" version = "2.3.7"
description = "The comprehensive WSGI web application library." description = "The comprehensive WSGI web application library."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "Werkzeug-2.3.6-py3-none-any.whl", hash = "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890"}, {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"},
{file = "Werkzeug-2.3.6.tar.gz", hash = "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330"}, {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"},
] ]
[package.dependencies] [package.dependencies]
@ -1215,21 +1443,21 @@ files = [
[[package]] [[package]]
name = "zipp" name = "zipp"
version = "3.16.2" version = "3.17.0"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
{file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
] ]
[package.extras] [package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "96ec0d1f7b512afb05455262fa2de8c4f862bf68fdae513f8552dc30c6e5ab49" content-hash = "f3ff236ae81d76796722dcd183466f483b6d5aba6dbfcffc145747aa0bb36087"

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "OnlyLegs" name = "OnlyLegs"
version = "0.1.5" version = "0.1.7"
repository = "https://github.com/Fluffy-Bean/onlylegs" repository = "https://github.com/Fluffy-Bean/onlylegs"
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"
@ -16,8 +16,8 @@ Flask-Compress = "^1.13"
Flask-Caching = "1.10.1" Flask-Caching = "1.10.1"
Flask-Assets = "^2.0" Flask-Assets = "^2.0"
Flask-Login = "^0.6.2" Flask-Login = "^0.6.2"
python-dotenv = "^0.21.0" python-dotenv = "1.0.0"
gunicorn = "^20.1.0" gunicorn = "^21.2.0"
pyyaml = "^6.0" pyyaml = "^6.0"
colorthief = "^0.2.1" colorthief = "^0.2.1"
Pillow = "^9.4.0" Pillow = "^9.4.0"
@ -28,6 +28,7 @@ cssmin = "^0.2.0"
pylint = "^2.16.3" pylint = "^2.16.3"
black = "^23.3.0" black = "^23.3.0"
cachetools = "^5.3.0" cachetools = "^5.3.0"
material-color-utilities-python = "^0.1.5"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

View file

@ -1,33 +0,0 @@
"""
Startup arguments for the OnlyLegs gallery
-p, --port: Port to run on (default: 5000)
-a, --address: Address to run on (default: 127.0.0.0)
-w, --workers: Number of workers to run (default: 4)
-d, --debug: Run as Flask app in debug mode (default: False)
-S, --scream: Show verbose output (default: False)
-h, --help: Show a help message
"""
import argparse
parser = argparse.ArgumentParser(description="Run the OnlyLegs gallery")
parser.add_argument("-p", "--port", type=int, default=5000, help="Port to run on")
parser.add_argument(
"-a", "--address", type=str, default="127.0.0.0", help="Address to run on"
)
parser.add_argument(
"-w", "--workers", type=int, default=4, help="Number of workers to run"
)
parser.add_argument(
"-d", "--debug", action="store_true", help="Run as Flask app in debug mode"
)
args = parser.parse_args()
PORT = args.port
ADDRESS = args.address
WORKERS = args.workers
DEBUG = args.debug

View file

@ -1,156 +0,0 @@
"""
OnlyLegs - Setup
Runs when the app detects that there is no user directory
"""
import os
import logging
import re
import platformdirs
import yaml
USER_DIR = platformdirs.user_config_dir("onlylegs")
class Configuration:
"""
Setup the application on first run
"""
def __init__(self):
"""
Main setup function
"""
print("Running startup checks...")
# Check if the user directory exists
if not os.path.exists(USER_DIR):
self.make_dir()
# Check if the .env file exists
if not os.path.exists(os.path.join(USER_DIR, ".env")):
self.make_env()
# Check if the conf.yml file exists
if not os.path.exists(os.path.join(USER_DIR, "conf.yml")):
self.make_yaml()
# Load the config files
self.logging_config()
@staticmethod
def make_dir():
"""
Create the user directory
"""
os.makedirs(USER_DIR)
os.makedirs(os.path.join(USER_DIR, "instance"))
os.makedirs(os.path.join(USER_DIR, "media"))
os.makedirs(os.path.join(USER_DIR, "media", "uploads"))
os.makedirs(os.path.join(USER_DIR, "media", "cache"))
os.makedirs(os.path.join(USER_DIR, "media", "pfp"))
print("Created user directory at:", USER_DIR)
@staticmethod
def make_env():
"""
Create the .env file with default values
"""
env_conf = {
"FLASK_SECRET": os.urandom(32).hex(),
}
with open(os.path.join(USER_DIR, ".env"), encoding="utf-8", mode="w+") as file:
for key, value in env_conf.items():
file.write(f"{key}={value}\n")
print(
"""
####################################################
# A NEW KEY WAS GENERATED FOR YOU! PLEASE NOTE #
# DOWN THE FLASK_SECRET KEY LOCATED IN YOUR #
# .config/onlylegs/.env FOLDER! LOOSING THIS KEY #
# WILL RESULT IN YOU BEING UNABLE TO LOG IN! #
####################################################
"""
)
@staticmethod
def make_yaml():
"""
Create the YAML config file with default values
"""
is_correct = False
email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
username_regex = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
print("\nNo config file found, please enter the following information:")
while not is_correct:
username = input("Admin username: ")
name = input("Admin name: ")
email = input("Admin email: ")
# Check if the values are valid
if not username or not username_regex.match(username):
print("Username is invalid!")
continue
if not name:
print("Name is invalid!")
continue
if not email or not email_regex.match(email):
print("Email is invalid!")
continue
# Check if user is happy with the values
if input("Is this correct? (y/n): ").lower() == "y":
is_correct = True
yaml_conf = {
"admin": {
"name": name,
"username": username,
"email": email,
},
"upload": {
"allowed-extensions": {
"jpg": "jpeg",
"jpeg": "jpeg",
"png": "png",
"webp": "webp",
},
"max-size": 69,
"max-load": 50,
"rename": "GWA_{{username}}_{{time}}",
},
"website": {
"name": "OnlyLegs",
"motto": "A gallery built for fast and simple image management!",
"language": "en",
},
}
with open(
os.path.join(USER_DIR, "conf.yml"), encoding="utf-8", mode="w+"
) as file:
yaml.dump(yaml_conf, file, default_flow_style=False)
print(
"Generated config file, you can change these values in the settings of the app"
)
@staticmethod
def logging_config():
"""
Set the logging config
"""
logging.getLogger("werkzeug").disabled = True
logging.basicConfig(
filename=os.path.join(USER_DIR, "only.log"),
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",
encoding="utf-8",
)

View file

@ -1,35 +0,0 @@
"""
Gunicorn configuration file
"""
from gunicorn.app.base import Application
from gunicorn import util
class OnlyLegs(Application):
"""
Gunicorn application
"""
# TODO: Make this not shit, thanks
def __init__(self, options={}): # skipcq: PYL-W0231 # pylint: disable=W0231
self.usage = None
self.callable = None
self.options = options
self.do_load_config()
def init(self, *args):
"""
Initialize the application
"""
cfg = {}
for setting, value in self.options.items():
if setting.lower() in self.cfg.settings and value is not None:
cfg[setting.lower()] = value
return cfg
@staticmethod
def prog(): # skipcq: PYL-E0202 # pylint: disable=E0202, C0116
return "OnlyLegs"
def load(self):
return util.import_app("onlylegs.app:app")