diff --git a/README.md b/README.md index ac0254c..35218ee 100644 --- a/README.md +++ b/README.md @@ -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"> <img src=".github/images/OnlyLegs.png" width="200" height="200"/> <div id="user-content-toc"> diff --git a/onlylegs/api.py b/onlylegs/api.py index 44c187c..0c68f82 100644 --- a/onlylegs/api.py +++ b/onlylegs/api.py @@ -3,7 +3,6 @@ Onlylegs - API endpoints """ import os import pathlib -import re import logging from uuid import uuid4 @@ -23,88 +22,13 @@ from flask_login import login_required, current_user from colorthief import ColorThief 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 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"]) def media(path): """ diff --git a/onlylegs/app.py b/onlylegs/app.py index 0dd4349..5e1e632 100644 --- a/onlylegs/app.py +++ b/onlylegs/app.py @@ -1,136 +1,179 @@ """ 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 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_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.config import INSTANCE_DIR, MIGRATIONS_DIR -from onlylegs.models import Users -from onlylegs.views import ( - index as view_index, - image as view_image, - group as view_group, - settings as view_settings, - profile as view_profile, +from onlylegs.config import ( + INSTANCE_DIR, + MIGRATIONS_DIR, + APPLICATION_ROOT, + DATABASE_NAME, ) -from onlylegs import api -from onlylegs import auth as view_auth -from onlylegs import filters +from onlylegs.models import Users + +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.config.from_pyfile("config.py") -# DATABASE db.init_app(app) migrate.init_app(app, db, directory=MIGRATIONS_DIR) -# If database file doesn't exist, create it -if not os.path.exists(os.path.join(INSTANCE_DIR, "gallery.sqlite3")): - print("Creating database") - with app.app_context(): - db.create_all() +create_db() - register_user = Users( - username=app.config["ADMIN_CONF"]["username"], - email=app.config["ADMIN_CONF"]["email"], - password=generate_password_hash("changeme!", method="sha256"), - ) - db.session.add(register_user) - db.session.commit() +set_logger() +set_login_manager() +page_assets() +handle_errors() +register_blueprints() - 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) compress.init_app(app) -# Yupee! We got there :3 -print("Done!") logging.info("Gallery started successfully!") diff --git a/onlylegs/auth.py b/onlylegs/auth.py index c3d0698..4394b31 100644 --- a/onlylegs/auth.py +++ b/onlylegs/auth.py @@ -105,5 +105,5 @@ def logout(): Clear the current session, including the stored user id """ logout_user() - flash(["Goodbye!!!", "4"]) + flash("Goodbye!!!", "4") return redirect(url_for("gallery.index")) diff --git a/onlylegs/config.py b/onlylegs/config.py index 06cd271..a24c24d 100644 --- a/onlylegs/config.py +++ b/onlylegs/config.py @@ -6,42 +6,43 @@ import platformdirs import importlib.metadata from dotenv import load_dotenv from yaml import safe_load +from utils import startup + + +# App Sanity Checks +startup.check_dirs() +startup.check_env() +startup.check_conf() # Set dirs -user_dir = platformdirs.user_config_dir("onlylegs") -instance_dir = os.path.join(user_dir, "instance") +APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs") +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 -# print("Loading environment variables...") -load_dotenv(os.path.join(user_dir, ".env")) +# Load env and config files +load_dotenv(os.path.join(APPLICATION_ROOT, ".env")) -# Load config from user dir -# print("Loading config...") -with open(os.path.join(user_dir, "conf.yml"), encoding="utf-8", mode="r") as file: +config_file = os.path.join(APPLICATION_ROOT, "conf.yml") +with open(config_file, encoding="utf-8", mode="r") as file: conf = safe_load(file) - # Flask config SECRET_KEY = os.environ.get("FLASK_SECRET") -SQLALCHEMY_DATABASE_URI = "sqlite:///gallery.sqlite3" MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"] 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 ADMIN_CONF = conf["admin"] UPLOAD_CONF = conf["upload"] 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") diff --git a/onlylegs/filters.py b/onlylegs/filters.py index 765003b..1f4515b 100644 --- a/onlylegs/filters.py +++ b/onlylegs/filters.py @@ -4,6 +4,7 @@ Custom Jinja2 filters """ from flask import Blueprint from onlylegs.utils import colour as colour_utils +import colorsys blueprint = Blueprint("filters", __name__) @@ -17,4 +18,32 @@ def colour_contrast(colour): "color: var(--fg-white);" or "color: var(--fg-black);" """ 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 diff --git a/onlylegs/models.py b/onlylegs/models.py index 565b130..2aca5c0 100644 --- a/onlylegs/models.py +++ b/onlylegs/models.py @@ -64,7 +64,7 @@ class Exif(db.Model): picture_id = db.Column(db.Integer, db.ForeignKey("pictures.id")) 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): diff --git a/onlylegs/static/js/imagePage.js b/onlylegs/static/js/imagePage.js index a1eb24c..43dd207 100644 --- a/onlylegs/static/js/imagePage.js +++ b/onlylegs/static/js/imagePage.js @@ -14,47 +14,6 @@ function imageFullscreen() { } 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, [ { 'value': 'Edit', @@ -62,7 +21,8 @@ function imageShowOptionsPopup(obj) { dissmissContextMenu(); imageEditPopup(); }, - 'type': 'critical' + 'type': 'critical', + 'icon': '<i class="ph-fill ph-pencil"></i>' }, { 'value': 'Delete', @@ -70,7 +30,8 @@ function imageShowOptionsPopup(obj) { dissmissContextMenu(); imageDeletePopup(); }, - 'type': 'critical' + 'type': 'critical', + 'icon': '<i class="ph-fill ph-trash"></i>' } ], 'button') } diff --git a/onlylegs/static/js/uploadTab.js b/onlylegs/static/js/uploadTab.js index a27e57f..2526a8a 100644 --- a/onlylegs/static/js/uploadTab.js +++ b/onlylegs/static/js/uploadTab.js @@ -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', () => { // Function to upload images 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 uploadForm = uploadTab.querySelector('#uploadForm'); - // let jobList = document.querySelector(".upload-jobs"); const fileDrop = uploadForm.querySelector('.fileDrop-block'); const fileDropTitle = fileDrop.querySelector('.status'); @@ -228,54 +201,6 @@ document.addEventListener('DOMContentLoaded', () => { formData.append("description", fileDescription.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', { method: 'POST', body: formData diff --git a/onlylegs/static/sass/components/banner.sass b/onlylegs/static/sass/components/banner.sass index 037e38d..724a5ee 100644 --- a/onlylegs/static/sass/components/banner.sass +++ b/onlylegs/static/sass/components/banner.sass @@ -2,7 +2,7 @@ .banner-small width: 100% position: relative - color: RGB($fg-white) + color: var(--foreground-white) .link padding: 0.1rem 0.3rem @@ -10,15 +10,15 @@ text-decoration: none font-weight: 500 - background-color: RGB($fg-white) - color: RGB($fg-black) - border-radius: $rad-inner + background-color: var(--foreground-white) + color: var(--foreground-black) + border-radius: calc(var(--radius) / 2) cursor: pointer &:hover - background-color: RGB($fg-black) - color: RGB($fg-white) + background-color: var(--foreground-black) + color: var(--foreground-white) .banner height: 30rem @@ -26,7 +26,8 @@ img position: absolute - inset: 0 + top: 0 + left: 0 width: 100% height: 100% @@ -43,7 +44,7 @@ width: 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 @@ -79,7 +80,7 @@ font-size: 6.9rem font-weight: 700 - color: RGB($primary) + color: var(--primary) .banner-info grid-area: info @@ -104,8 +105,8 @@ width: 6.9rem height: 6.9rem - background-color: RGB($primary) - border-radius: $rad + background-color: var(--primary) + border-radius: var(--rad) overflow: hidden .banner-small @@ -144,7 +145,7 @@ font-weight: 700 font-size: 1.5rem - color: RGB($primary) + color: var(--primary) .banner-info margin-right: 0.6rem @@ -156,7 +157,7 @@ margin-left: auto width: auto -@media (max-width: $breakpoint) +@media (max-width: 800px) .banner, .banner-small &::after @@ -168,7 +169,7 @@ max-height: 30vh .banner-filter - background: linear-gradient(to bottom, RGB($bg-100), transparent) + background: linear-gradient(to top, var(--background-100), transparent) .banner-content padding: 0.5rem @@ -192,7 +193,7 @@ display: none .pill-row - margin-top: 0rem + margin-top: 0 .banner-picture margin: 0 auto diff --git a/onlylegs/static/sass/components/buttons/block.sass b/onlylegs/static/sass/components/buttons/block.sass index f16f96e..7994647 100644 --- a/onlylegs/static/sass/components/buttons/block.sass +++ b/onlylegs/static/sass/components/buttons/block.sass @@ -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 padding: 0.4rem 0.7rem @@ -26,19 +15,17 @@ font-weight: 400 text-align: center - background-color: RGBA($white, 0.1) - color: RGB($white) + background-color: var(--white-transparent) + color: var(--white) border: none - border-radius: $rad-inner - // box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2) + border-radius: calc(var(--rad) / 2) outline: none cursor: pointer transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out, box-shadow 0.15s ease-in-out &:hover, &:focus-visible - background-color: RGBA($white, 0.2) - // box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3) + background-color: var(--white-transparent) &.transparent background-color: transparent @@ -47,20 +34,50 @@ text-decoration: underline &.primary - @include btn-block($primary) - &.critical - @include btn-block($critical) - &.warning - @include btn-block($warning) + background-color: var(--primary-transparent) + color: var(--primary) + + &:hover, &:focus-visible + background-color: var(--primary) + color: var(--black) &.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 - @include btn-block($info) + background-color: var(--info-transparent) + color: var(--info) + + &:hover, &:focus-visible + background-color: var(--info) + color: var(--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 - color: RGB($fg-dim) + color: var(--foreground-gray) cursor: unset .input-checkbox @@ -77,7 +94,7 @@ font-weight: 400 text-align: left - color: RGB($fg-white) + color: var(--foreground-white) .input-block padding: 0.4rem 0.7rem @@ -95,28 +112,32 @@ font-weight: 400 text-align: left - background-color: RGBA($white, 0.1) - color: RGB($white) + background-color: var(--white-transparent) + color: var(--white) border: none - border-bottom: 3px solid RGBA($white, 0.1) - border-radius: $rad-inner - // box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2) + border-bottom: 3px solid var(--white-transparent) + border-radius: calc(var(--rad) / 2) outline: none cursor: pointer transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out &:not(:focus):not([value=""]):not(:placeholder-shown) - border-color: RGBA($white, 0.3) + border-color: var(--white-transparent) &:hover - border-color: RGBA($white, 0.3) + border-color: var(--white-transparent) &:focus - border-color: RGB($primary) + border-color: var(--primary) &.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 padding: 1rem 1.25rem @@ -136,11 +157,10 @@ font-weight: 400 text-align: center - background-color: RGBA($white, 0.1) - color: RGB($white) + background-color: var(--white-transparent) + color: var(--white) border: none - border-radius: $rad-inner - // box-shadow: 0 1px 0 RGBA($black, 0.2), 0 -1px 0 RGBA($white, 0.2) + border-radius: calc(var(--rad) / 2) outline: none cursor: pointer @@ -164,24 +184,21 @@ overflow: hidden &:hover, &:focus-visible - background-color: RGBA($white, 0.2) - color: RGB($white) - // box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3) + background-color: var(--white-transparent) + color: var(--white) &.active - background-color: RGBA($primary, 0.2) - color: RGB($primary) - // box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($primary, 0.3) + background-color: var(--primary-transparent) + color: var(--primary) &.edging - background-color: RGBA($white, 0.2) - color: RGB($white) - // box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($white, 0.3) + background-color: var(--white-transparent) + color: var(--white) 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 - background-color: RGBA($critical, 0.2) - color: RGB($critical) - // box-shadow: 0 1px 0 RGBA($black, 0.3), 0 -1px 0 RGBA($critical, 0.3) + background-color: var(--danger) + color: var(--black) diff --git a/onlylegs/static/sass/components/buttons/info-button.sass b/onlylegs/static/sass/components/buttons/info-button.sass deleted file mode 100644 index 4805c59..0000000 --- a/onlylegs/static/sass/components/buttons/info-button.sass +++ /dev/null @@ -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 diff --git a/onlylegs/static/sass/components/buttons/pill.sass b/onlylegs/static/sass/components/buttons/pill.sass index 3767c01..25fc812 100644 --- a/onlylegs/static/sass/components/buttons/pill.sass +++ b/onlylegs/static/sass/components/buttons/pill.sass @@ -16,9 +16,8 @@ display: flex - background-color: RGB($bg-200) - border-radius: $rad - // box-shadow: 0 1px 0 RGB($bg-100), 0 -1px 0 RGB($bg-300) + background-color: var(--background-200) + border-radius: var(--rad) .pill-text margin: 0 @@ -37,9 +36,9 @@ font-size: 1rem font-weight: 400 - background-color: RGB($bg-200) - color: RGB($fg-white) - border-radius: $rad + background-color: var(--background-200) + color: var(--foreground-white) + border-radius: var(--rad) .pill-item margin: 0 @@ -58,42 +57,41 @@ border: none background-color: transparent - color: RGB($fg-white) + color: var(--foreground-white) i font-size: 1.25rem &:hover cursor: pointer + color: var(--primary) - color: RGB($primary) - - &.disabled, &:disabled - color: RGB($fg-dim) - cursor: unset + &:disabled, &[disabled], &.disabled + color: var(--foreground-gray) + cursor: default .pill__critical - color: RGB($critical) + color: var(--danger) span - background: RGB($critical) - color: RGB($fg-white) + background: var(--danger) + color: var(--foreground-white) i - color: RGB($critical) + color: var(--danger) &:hover - color: RGB($fg-white) + color: var(--foreground-white) .pill__info - color: RGB($info) + color: var(--info) span - color: RGB($info) + color: var(--info) &:hover - color: RGB($fg-white) + color: var(--foreground-white) -@media (max-width: $breakpoint) +@media (max-width: var(--breakpoint)) .tool-tip display: none \ No newline at end of file diff --git a/onlylegs/static/sass/components/buttons/top-of-page.sass b/onlylegs/static/sass/components/buttons/top-of-page.sass index 918356d..dd356ba 100644 --- a/onlylegs/static/sass/components/buttons/top-of-page.sass +++ b/onlylegs/static/sass/components/buttons/top-of-page.sass @@ -13,9 +13,9 @@ justify-content: center align-items: center - background-color: RGB($bg-300) - color: RGB($fg-white) - border-radius: $rad + background-color: var(--background-100) + color: var(--foreground-white) + border-radius: calc(var(--rad) / 2) border: none opacity: 0 @@ -28,12 +28,12 @@ font-size: 1.25rem &:hover - color: RGB($primary) + color: var(--primary) &.show right: 0.75rem opacity: 1 -@media (max-width: $breakpoint) +@media (max-width: 800px) .top-of-page bottom: 4.25rem diff --git a/onlylegs/static/sass/components/context-menu.sass b/onlylegs/static/sass/components/context-menu.sass index 15ab78b..8d4b4aa 100644 --- a/onlylegs/static/sass/components/context-menu.sass +++ b/onlylegs/static/sass/components/context-menu.sass @@ -25,13 +25,13 @@ align-items: flex-start gap: 0.25rem - background-color: RGB($bg-300) - border: 1px solid RGB($bg-200) + background-color: var(--background-300) + border: 1px solid var(--background-200) border-radius: 6px 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 opacity: 0.5 transform: scale(0, 0) @@ -46,7 +46,7 @@ text-align: center font-size: 1.2rem font-weight: 400 - color: RGB($fg-white) + color: var(--foreground-white) .contextMenuItem margin: 0 @@ -61,22 +61,22 @@ align-items: center gap: 0.5rem - background-color: RGB($bg-300) - color: RGB($fg-white) + background-color: var(--background-300) + color: var(--foreground-white) border: none border-radius: 3px cursor: pointer .contextMenuItem:hover - background-color: RGB($bg-200) + background-color: var(--background-200) .contextMenuItem__critical - color: RGB($critical) + color: var(--danger) .contextMenuItem__warning - color: RGB($warning) + color: var(--warning) .contextMenuItem__success - color: RGB($success) + color: var(--success) .contextMenuItem__info - color: RGB($primary) + color: var(--primary) .contextMenuText margin: 0 @@ -104,7 +104,7 @@ height: 1px border: none - background-color: RGB($bg-200) + background-color: var(--background-200) .contextMenu__show opacity: 1 diff --git a/onlylegs/static/sass/components/gallery.sass b/onlylegs/static/sass/components/gallery.sass index 87d63ca..081af88 100644 --- a/onlylegs/static/sass/components/gallery.sass +++ b/onlylegs/static/sass/components/gallery.sass @@ -12,26 +12,21 @@ font-weight: 700 .gallery-grid - margin: 0 - padding: 0.35rem - width: 100% display: grid grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) + gap: 0.5rem .gallery-item - margin: 0.35rem - padding: 0 - position: relative - border-radius: $rad-inner - box-shadow: 0 0.15rem 0.4rem 0.1rem RGBA($bg-100, 0.4) + border-radius: calc(var(--rad) / 2) + box-shadow: 0 0.15rem 0.4rem 0.1rem var(--black-transparent) box-sizing: border-box overflow: hidden - transition: box-shadow 0.2s cubic-bezier(.79, .14, .15, .86) + transition: box-shadow 0.2s var(--animation-smooth) .image-filter margin: 0 @@ -48,11 +43,11 @@ flex-direction: column 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 z-index: +4 - transition: opacity 0.2s cubic-bezier(.79, .14, .15, .86) + transition: opacity 0.2s var(--animation-smooth) .image-title, .image-subtitle @@ -63,8 +58,8 @@ text-overflow: ellipsis overflow: hidden - color: RGB($fg-white) - text-shadow: 0px 0px 2px RGB($fg-black) + color: var(--foreground-white) + text-shadow: 0 0 2px var(--foreground-black) .image-title font-size: 0.9rem @@ -84,21 +79,20 @@ object-fit: cover object-position: center - background-color: RGB($bg-bright) + background-color: var(--background-bright) &: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 opacity: 1 .group-item - margin: 0.35rem padding: 0 position: relative - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) box-sizing: border-box overflow: hidden @@ -119,7 +113,7 @@ flex-direction: column 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 @@ -132,8 +126,8 @@ text-overflow: ellipsis overflow: hidden - color: RGB($fg-white) - text-shadow: 0px 0px 2px RGB($fg-black) + color: var(--foreground-white) + text-shadow: 0 0 2px var(--foreground-black) .image-title font-size: 0.9rem @@ -165,11 +159,11 @@ object-fit: cover object-position: center - background-color: RGB($bg-bright) - border-radius: $rad-inner - box-shadow: 0 0 0.4rem 0.25rem RGBA($bg-100, 0.1) + background-color: var(--background-800) + border-radius: calc(var(--rad) / 2) + 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 .data-1 @@ -224,5 +218,5 @@ grid-template-columns: auto auto auto .gallery-item - margin: 0.35rem + margin: 0.1rem position: relative \ No newline at end of file diff --git a/onlylegs/static/sass/components/image-view.sass b/onlylegs/static/sass/components/image-view.sass index 1439670..249f04c 100644 --- a/onlylegs/static/sass/components/image-view.sass +++ b/onlylegs/static/sass/components/image-view.sass @@ -1,11 +1,11 @@ .info-container - padding: 0.5rem 0 0 0.5rem + padding: 0.5rem 0 0.5rem 0.5rem width: 27rem position: absolute top: 0 left: 0 bottom: 0 - background-image: linear-gradient(90deg, $bg-transparent, transparent) + background-image: linear-gradient(90deg, var(--background-shade), transparent) overflow-y: auto transition: left 0.3s cubic-bezier(0.76, 0, 0.17, 1) z-index: 2 @@ -18,7 +18,7 @@ left: -27rem @media (max-width: 1100px) .info-container - padding: 0 0.5rem 0 0.5rem + padding: 0 width: 100% position: relative background: none @@ -32,9 +32,9 @@ details padding: 0.5rem display: flex flex-direction: column - background-color: RGB($bg-300) - color: RGB($fg-white) - border-radius: $rad + background-color: var(--background-300) + color: var(--foreground-white) + border-radius: var(--rad) overflow: hidden summary @@ -44,7 +44,7 @@ details justify-content: flex-start align-items: center position: relative - color: RGB($primary) + color: var(--primary) > i margin-right: 0 @@ -58,13 +58,6 @@ details font-size: 1.1rem font-weight: 500 - &[open] - summary - margin-bottom: 0.5rem - - > i.collapse-indicator - transform: rotate(90deg) - p margin: 0 padding: 0 @@ -79,7 +72,7 @@ details margin: 0 padding: 0 - color: RGB($primary) + color: var(--primary) cursor: pointer text-decoration: none @@ -91,7 +84,7 @@ details width: 1.1rem height: 1.1rem - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) object-fit: cover @@ -137,6 +130,16 @@ details tr:last-of-type td padding-bottom: 0 + &[open] + summary + margin-bottom: 0.5rem + + > i.collapse-indicator + transform: rotate(90deg) + + &:last-of-type + margin-bottom: 0 + .img-colours width: 100% @@ -154,7 +157,7 @@ details justify-content: center align-items: center - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) border: none i @@ -197,7 +200,9 @@ details max-height: 100% object-fit: contain object-position: center - border-radius: $rad + border-radius: var(--rad) + + transition: border-radius 0.3s cubic-bezier(0.76, 0, 0.17, 1) &.collapsed padding: 0 @@ -207,6 +212,7 @@ details border-radius: 0 @media (max-width: 1100px) .image-container + padding: 0 0 0.5rem 0 position: relative left: 0 @@ -218,17 +224,17 @@ details max-height: 69vh &.collapsed - padding: 0.5rem + padding: 0 0 0.5rem 0 left: 0 picture img - border-radius: $rad + border-radius: var(--rad) .background position: absolute inset: 0 - background-color: RGB($bg-300) - background-image: linear-gradient(to right, RGB($bg-400) 15%, RGB($bg-200) 35%, RGB($bg-400) 50%) + background-color: var(--background-300) + background-image: linear-gradient(to right, var(--background-400) 15%, var(--background-200) 35%, var(--background-400) 50%) background-size: 1000px 640px animation: imgLoading 1.8s linear infinite forwards user-select: none @@ -240,7 +246,7 @@ details inset: 0 width: 100% height: 100% - background-color: RGB($fg-white) + background-color: var(--foreground-white) filter: blur(3rem) saturate(1.2) brightness(0.7) transform: scale(1.1) object-fit: cover diff --git a/onlylegs/static/sass/components/navigation.sass b/onlylegs/static/sass/components/navigation.sass index 760a7d3..9ea4332 100644 --- a/onlylegs/static/sass/components/navigation.sass +++ b/onlylegs/static/sass/components/navigation.sass @@ -42,27 +42,27 @@ nav > i padding: 0.5rem font-size: 1.3rem - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) color: inherit > .nav-pfp padding: 0.4rem width: 2.3rem height: 2.3rem - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) img width: 100% height: 100% object-fit: cover - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) &:hover > i, .nav-pfp - background: RGBA($fg-white, 0.1) + background: var(--white-transparent) &.selected - color: RGB($primary) + color: var(--primary) &::before content: '' @@ -76,9 +76,9 @@ nav height: calc(100% - 1rem) background-color: currentColor - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) -@media (max-width: $breakpoint) +@media (max-width: 800px) nav width: 100vw height: 3.5rem @@ -91,7 +91,7 @@ nav bottom: 0 left: 0 - background-color: RGB($background) + background-color: var(--background-100) > span display: none diff --git a/onlylegs/static/sass/components/notification.sass b/onlylegs/static/sass/components/notification.sass index feea589..d7a3de5 100644 --- a/onlylegs/static/sass/components/notification.sass +++ b/onlylegs/static/sass/components/notification.sass @@ -1,23 +1,9 @@ @keyframes notificationTimeout 0% - left: -100% - height: 3px - 90% - left: 0% - height: 3px - 95% - left: 0% - height: 0 + width: 0 100% - left: 0% - height: 0 - -@mixin notification($color) - color: RGB($color) + width: 100% - &::after - background-color: RGB($color) - .notifications margin: 0 padding: 0 @@ -46,9 +32,9 @@ position: relative - background-color: RGB($bg-300) - border-radius: $rad-inner - color: RGB($fg-white) + background-color: var(--background-400) + border-radius: calc(var(--rad) / 2) + color: var(--foreground-white) opacity: 0 transform: scale(0.8) @@ -60,26 +46,34 @@ &::after content: "" - width: 100% + width: 0 height: 3px position: absolute - bottom: 0px - left: 0px + bottom: 0 + left: 0 - background-color: RGB($fg-white) + background-color: var(--foreground-white) z-index: +2 - animation: notificationTimeout 5.1s linear + animation: notificationTimeout 5.1s ease-out forwards &.success - @include notification($success) + color: var(--success) + &::after + background-color: var(--success) &.warning - @include notification($warning) + color: var(--warning) + &::after + background-color: var(--warning) &.critical - @include notification($critical) + color: var(--danger) + &::after + background-color: var(--danger) &.info - @include notification($info) + color: var(--info) + &::after + background-color: var(--info) &.show opacity: 1 @@ -89,7 +83,7 @@ margin: 0 max-height: 0 opacity: 0 - transform: translateX(100%) + transform: translateY(1rem) transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out .sniffle__notification-icon @@ -103,7 +97,7 @@ justify-content: center align-items: center - background-color: RGB($bg-200) + background-color: var(--background-300) i font-size: 1.25rem @@ -125,7 +119,7 @@ line-height: 1 text-align: left -@media (max-width: $breakpoint) +@media (max-width: 800px) .notifications bottom: 3.8rem width: calc(100vw - 0.6rem) @@ -133,10 +127,6 @@ .sniffle__notification width: 100% - - &.hide - opacity: 0 - transform: translateY(1rem) .sniffle__notification-time width: 100% diff --git a/onlylegs/static/sass/components/pop-up.sass b/onlylegs/static/sass/components/pop-up.sass index 0e6974b..9542f13 100644 --- a/onlylegs/static/sass/components/pop-up.sass +++ b/onlylegs/static/sass/components/pop-up.sass @@ -7,7 +7,7 @@ display: none - background-color: $bg-transparent + background-color: var(--background-shade) opacity: 0 z-index: 101 @@ -38,12 +38,12 @@ display: flex flex-direction: column - background-color: RGB($bg-200) - border-radius: $rad + background-color: var(--background-400) + border-radius: var(--rad) overflow: hidden z-index: +2 - transition: transform 0.2s $animation-smooth + transition: transform 0.2s var(--animation-bounce) .pop-up-header margin: 0 0 0.5rem 0 @@ -73,7 +73,7 @@ font-weight: 700 text-align: left - color: RGB($fg-white) + color: var(--foreground-white) p margin: 0 @@ -84,7 +84,7 @@ font-weight: 400 text-align: left - color: RGB($fg-white) + color: var(--foreground-white) svg width: 1rem @@ -94,7 +94,7 @@ vertical-align: middle a, .link - color: RGB($primary) + color: var(--primary) cursor: pointer text-decoration: none @@ -127,15 +127,13 @@ justify-content: flex-end gap: 0.5rem - // background-color: RGB($bg-100) - &.active opacity: 1 .pop-up-wrapper transform: translate(-50%, 50%) scale(1) -@media (max-width: $breakpoint) +@media (max-width: 800px) .pop-up .pop-up-wrapper max-width: calc(100% - 0.75rem) diff --git a/onlylegs/static/sass/components/settings.sass b/onlylegs/static/sass/components/settings.sass index 8587a76..1bf56e0 100644 --- a/onlylegs/static/sass/components/settings.sass +++ b/onlylegs/static/sass/components/settings.sass @@ -9,10 +9,10 @@ justify-content: center gap: 1rem - background-color: RGB($bg-400) - color: RGB($fg-white) - border: 2px solid RGB($bg-200) - border-radius: $rad-inner + background-color: var(--background-400) + color: var(--foreground-white) + border: 2px solid var(--background-200) + border-radius: calc(var(--rad) / 2) h2 margin: 0 diff --git a/onlylegs/static/sass/components/tags.sass b/onlylegs/static/sass/components/tags.sass index 6da6e67..b45b630 100644 --- a/onlylegs/static/sass/components/tags.sass +++ b/onlylegs/static/sass/components/tags.sass @@ -11,10 +11,10 @@ font-weight: 500 text-decoration: none - border-radius: $rad-inner + border-radius: calc(var(--rad) / 2) border: none - background-color: RGBA($primary, 0.1) - color: RGB($primary) + background-color: var(--primary-transparent) + color: var(--primary) cursor: pointer transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out @@ -23,4 +23,4 @@ font-size: 1.15rem &:hover - background-color: RGBA($primary, 0.2) + background-color: var(--primary-transparent) diff --git a/onlylegs/static/sass/components/upload-panel.sass b/onlylegs/static/sass/components/upload-panel.sass index ed090f0..e4fb816 100644 --- a/onlylegs/static/sass/components/upload-panel.sass +++ b/onlylegs/static/sass/components/upload-panel.sass @@ -9,11 +9,11 @@ height: 100vh background-color: transparent - color: RGB($fg-white) + color: var(--foreground-white) overflow: hidden 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 margin: 0 @@ -66,11 +66,11 @@ flex-direction: column gap: 1rem - background-color: RGB($bg-200) + background-color: var(--background-200) 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 display: none @@ -94,21 +94,21 @@ left: 50% transform: translate(-50%, -50%) - background-color: RGB($bg-400) - border-radius: $rad-inner + background-color: var(--background-400) + border-radius: calc(var(--rad) / 2) - transition: width 0.25s $animation-bounce + transition: width 0.25s var(--animation-smooth) &.dragging #dragIndicator::after width: 9rem - background-color: RGB($primary) + background-color: var(--primary) .upload-jobs display: flex flex-direction: column gap: 0.5rem - border-radius: $rad + border-radius: var(--rad) overflow-y: auto @@ -123,8 +123,8 @@ align-items: center gap: 0.5rem - background-color: RGB($bg-200) - border-radius: $rad + background-color: var(--background-200) + border-radius: var(--rad) overflow: hidden @@ -146,7 +146,7 @@ width: 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 margin: 0 @@ -159,51 +159,51 @@ font-size: 1rem font-weight: 600 - color: RGB($fg-white) + color: var(--foreground-white) z-index: +3 - transition: color 0.25s cubic-bezier(0.76, 0, 0.17, 1) + transition: color 0.25s var(--animation-smooth) .progress width: 100% - height: $rad-inner + height: calc(var(--rad) / 2) position: absolute bottom: 0 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 - transition: left 1s cubic-bezier(0.76, 0, 0.17, 1) + transition: left 1s var(--animation-smooth) &.critical .job__status, .progress - color: RGB($critical) + color: var(--critical) &.success .job__status - color: RGB($success) + color: var(--success) .progress height: 0 animation: none &.warning .job__status, .progress - color: RGB($warning) + color: var(--warning) &.critical, &.success, &.warning .progress height: 0 &.open - background-color: $bg-transparent + background-color: var(--background-shade) .container left: 0 -@media (max-width: $breakpoint) +@media (max-width: 800px) .upload-panel width: 100% height: calc(100vh - 3.5rem) @@ -219,7 +219,7 @@ left: 0 bottom: -100vh - border-radius: $rad $rad 0 0 + border-radius: var(--rad) var(--rad) 0 0 #dragIndicator display: block diff --git a/onlylegs/static/sass/style.sass b/onlylegs/static/sass/style.sass index c363234..159aa1e 100644 --- a/onlylegs/static/sass/style.sass +++ b/onlylegs/static/sass/style.sass @@ -1,6 +1,7 @@ // Default theme for OnlyLegs by FluffyBean // Mockup link: https://www.figma.com/file/IMZT5kZr3sAngrSHSGu5di/OnlyLegs?node-id=0%3A1 + @import "variables" @import "animations" @@ -15,7 +16,6 @@ @import "components/gallery" @import "components/buttons/top-of-page" -@import "components/buttons/info-button" @import "components/buttons/pill" @import "components/buttons/block" @@ -24,17 +24,17 @@ * box-sizing: border-box - scrollbar-color: RGB($primary) transparent - font-family: $font + scrollbar-color: var(--primary) transparent + font-family: var(--font-family) ::-webkit-scrollbar width: 0.5rem ::-webkit-scrollbar-track - background: RGB($bg-200) + background: var(--background-200) ::-webkit-scrollbar-thumb - background: RGB($primary) + background: var(--primary) ::-webkit-scrollbar-thumb:hover - background: RGB($fg-white) + background: var(--foreground-white) html margin: 0 @@ -51,25 +51,35 @@ body display: grid grid-template-rows: auto 1fr auto - background-color: RGB($background) - color: RGB($foreground) + font-family: var(--font-family) + + background-color: var(--background-100) + color: var(--foreground-white) overflow-x: hidden -@media (max-width: $breakpoint) +@media (max-width: 800px) body padding: 0 0 3.5rem 0 main + margin: 0 0.5rem 0.5rem 0 + padding: 0.5rem display: flex flex-direction: column position: relative - background: RGBA($white, 1) - color: RGB($fg-black) - border-top-left-radius: $rad + background: var(--background-800) + color: var(--foreground-black) + border-radius: var(--rad) overflow: hidden -@media (max-width: $breakpoint) +@media (max-width: 800px) 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 min-height: 100% @@ -86,7 +96,7 @@ main font-weight: 900 text-align: center - color: $primary + color: var(--primary) p margin: 0 2rem @@ -95,7 +105,7 @@ main font-size: 1.25rem font-weight: 400 text-align: center -@media (max-width: $breakpoint) +@media (max-width: 800px) .error-page h1 font-size: 4.5rem diff --git a/onlylegs/static/sass/variables.sass b/onlylegs/static/sass/variables.sass index e3fcd83..a31a7bd 100644 --- a/onlylegs/static/sass/variables.sass +++ b/onlylegs/static/sass/variables.sass @@ -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 - --bg-dim: 16, 16, 16 - --bg-bright: 232, 227, 227 - --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%))} + --background-hsl-hue: 69 + --background-hsl-saturation: 25% - --fg-dim: 102, 102, 102 - --fg-white: 232, 227, 227 - --fg-black: 16, 16, 16 + --background-100: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%) + --background-200: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 8%) + --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 - --white: 232, 227, 227 - --red: 182, 100, 103 - --orange: 217, 140, 95 - --yellow: 217, 188, 140 - --green: 140, 151, 125 - --blue: 141, 163, 185 - --purple: 169, 136, 176 + --background-shade: hsl(var(--background-hsl-hue), var(--background-hsl-saturation), 6%, 0.5) + + --foreground-white: rgb(232, 227, 227) + --foreground-gray: rgb(102, 102, 102) + --foreground-black: rgb(16, 16, 16) + + --black: rgb(20, 20, 20) + --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) - --critical: var(--red) + --warning-transparent: var(--orange-transparent) + --danger: var(--red) + --danger-transparent: var(--red-transparent) --success: var(--green) + --success-transparent: var(--green-transparent) --info: var(--blue) + --info-transparent: var(--blue-transparent) - --rad: 0.5rem - --rad-inner: calc(var(--rad) / 2) + --rad: 0.4rem --animation-smooth: cubic-bezier(0.76, 0, 0.17, 1) --animation-bounce: cubic-bezier(.68,-0.55,.27,1.55) --breakpoint: 800px + + --font-family: 'Switzer', sans-serif diff --git a/onlylegs/templates/base.html b/onlylegs/templates/base.html index b580c7f..3fb4855 100644 --- a/onlylegs/templates/base.html +++ b/onlylegs/templates/base.html @@ -17,9 +17,7 @@ <meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}"> <!-- Fonts --> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <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"> + <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"> <!-- phosphor icons --> <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 "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 %} </head> <body> @@ -123,6 +138,10 @@ <script type="text/javascript"> keepSquare(); + {% for message in get_flashed_messages() %} + addNotification('{{ message[0] }}', {{ message[1] }}); + {% endfor %} + const times = document.querySelectorAll('.time'); for (let i = 0; i < times.length; i++) { // Remove milliseconds @@ -184,10 +203,6 @@ } } } - - {% for message in get_flashed_messages() %} - addNotification('{{ message[0] }}', {{ message[1] }}); - {% endfor %} </script> {% block script %}{% endblock %} diff --git a/onlylegs/templates/group.html b/onlylegs/templates/group.html index 67cfb1f..afaec5c 100644 --- a/onlylegs/templates/group.html +++ b/onlylegs/templates/group.html @@ -20,40 +20,9 @@ <style> {% if images %} - :root { --bg-100: {{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }} } - - body { - 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; - } + :root { + --background-hsl-hue: {{ images.0.colours.0 | hsl_hue }}; + --background-hsl-saturation: {{ images.0.colours.0 | hsl_saturation }}%; } {% endif %} </style> diff --git a/onlylegs/templates/image.html b/onlylegs/templates/image.html index edb8a12..725a363 100644 --- a/onlylegs/templates/image.html +++ b/onlylegs/templates/image.html @@ -18,6 +18,10 @@ </script> <style> + :root { + --background-hsl-hue: {{ image.colours.2 | hsl_hue }}; + --background-hsl-saturation: {{ image.colours.2 | hsl_saturation }}%; + } .background::after { 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)); @@ -30,15 +34,23 @@ <div class="banner-content"> <h1 class="banner-header">{{ config.WEBSITE_CONF.name }}</h1> <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> - <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> {% 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 %} </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> diff --git a/onlylegs/templates/profile.html b/onlylegs/templates/profile.html index 7880339..c0e42af 100644 --- a/onlylegs/templates/profile.html +++ b/onlylegs/templates/profile.html @@ -29,7 +29,7 @@ {% block header %} <div class="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 %} <img src="{{ url_for('static', filename='banner.png') }}" alt="Profile Banner" onload="imgFade(this)" style="opacity:0;"/> {% endif %} diff --git a/onlylegs/templates/settings.html b/onlylegs/templates/settings.html index 10840cb..521296a 100644 --- a/onlylegs/templates/settings.html +++ b/onlylegs/templates/settings.html @@ -16,50 +16,86 @@ {% endblock %} {% block content %} - <div class="info-tab" id="profileSettings" style="margin: 0.5rem 0.5rem 0 0.5rem"> - <div class="info-header"> - <i class="ph ph-info"></i> - <h2>Profile Settings</h2> - <button class="collapse-indicator"><i class="ph ph-caret-down"></i></button> - </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> + <details open> + <summary> + <i class="ph ph-info"></i><h2>Profile</h2><span style="width: 100%"></span> + <i class="ph ph-caret-down collapse-indicator"></i> + </summary> - <div class="info-tab" id="profileSettings" style="margin: 0.5rem 0.5rem 0 0.5rem"> - <div class="info-header"> - <i class="ph ph-info"></i> - <h2>Account Settings</h2> - <button class="collapse-indicator"><i class="ph ph-caret-down"></i></button> - </div> - <div class="info-table"> - <form method="POST" action="" enctype="multipart/form-data"> - <h3>Email</h3> - <input type="text" name="email" class="input-block" value="{{ current_user.email }}" /> - <input type="submit" value="Upload" class="btn-block"/> - </form> - </div> - </div> + <form method="POST" action="{{ url_for('settings.account_picture') }}" enctype="multipart/form-data"> + <h3>Profile Picture</h3> + <input type="file" name="file" tab-index="-1"/> + <button type="submit" class="btn-block">Change Profile Picture</button> + </form> + <form method="POST" action="{{ url_for('settings.account_banner') }}" enctype="multipart/form-data"> + <h3>Profile Banner</h3> + <input type="file" name="file" tab-index="-1"/> + <button type="submit" class="btn-block">Change Profile Banner</button> + </form> + <form method="POST" action="{{ url_for('settings.account_username') }}" enctype="multipart/form-data"> + <h3>Username</h3> + <input type="text" name="name" class="input-block" value="{{ current_user.username }}" /> + <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 %} {% block script %} - <script type="text/javascript"> - let infoTab = document.querySelectorAll('.info-tab'); + <script> + 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++) { - infoTab[i].querySelector('.collapse-indicator').addEventListener('click', function() { - infoTab[i].classList.toggle('collapsed'); - }); - } + hue.addEventListener('input', () => { + document.documentElement.style.setProperty('--background-hsl-hue', hue.value, 'important'); + }); + 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> {% endblock %} + diff --git a/onlylegs/utils/colour.py b/onlylegs/utils/colour.py index 491d4cc..7be8cc6 100644 --- a/onlylegs/utils/colour.py +++ b/onlylegs/utils/colour.py @@ -58,7 +58,7 @@ class Colour: s = 0.0 else: 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 = { r: (g - b) / d + (6 if g < b else 0), g: (b - r) / d + 2, diff --git a/onlylegs/utils/startup.py b/onlylegs/utils/startup.py new file mode 100644 index 0000000..b8e30eb --- /dev/null +++ b/onlylegs/utils/startup.py @@ -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", + ) diff --git a/onlylegs/views/settings.py b/onlylegs/views/settings.py index 389433b..092ed45 100644 --- a/onlylegs/views/settings.py +++ b/onlylegs/views/settings.py @@ -1,17 +1,191 @@ """ OnlyLegs - Settings page """ -from flask import Blueprint, render_template -from flask_login import login_required +import os +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.route("/") +@blueprint.route("/", methods=["GET"]) @login_required def general(): """ General settings page """ 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 diff --git a/poetry.lock b/poetry.lock index 8c1c926..2bf4c0d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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]] name = "alembic" -version = "1.11.2" +version = "1.12.0" description = "A database migration tool for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "alembic-1.11.2-py3-none-any.whl", hash = "sha256:7981ab0c4fad4fe1be0cf183aae17689fe394ff874fd2464adb774396faf0796"}, - {file = "alembic-1.11.2.tar.gz", hash = "sha256:678f662130dc540dac12de0ea73de9f89caea9dbea138f60ef6263149bf84657"}, + {file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"}, + {file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"}, ] [package.dependencies] @@ -24,14 +24,14 @@ tz = ["python-dateutil"] [[package]] name = "astroid" -version = "2.15.6" +version = "2.15.8" description = "An abstract syntax tree for Python with inference support." category = "main" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"}, - {file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"}, + {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, + {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, ] [package.dependencies] @@ -44,34 +44,34 @@ wrapt = [ [[package]] name = "black" -version = "23.7.0" +version = "23.9.1" description = "The uncompromising code formatter." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, - {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, - {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, - {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, - {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, - {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, - {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, - {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, - {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, - {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, - {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, + {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, + {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, ] [package.dependencies] @@ -81,7 +81,7 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" 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] colorama = ["colorama (>=0.4.3)"] @@ -103,96 +103,137 @@ files = [ [[package]] name = "brotli" -version = "1.0.9" +version = "1.1.0" description = "Python bindings for the Brotli compression library" category = "main" optional = false python-versions = "*" files = [ - {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"}, - {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"}, - {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"}, - {file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"}, - {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"}, - {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"}, - {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"}, - {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"}, - {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"}, - {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.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.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"}, - {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"}, - {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"}, - {file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"}, - {file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"}, - {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"}, - {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"}, - {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"}, - {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"}, - {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.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"}, - {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"}, - {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"}, - {file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"}, - {file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"}, - {file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"}, - {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"}, - {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"}, - {file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"}, - {file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"}, - {file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"}, - {file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"}, - {file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"}, - {file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"}, - {file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"}, - {file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"}, - {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"}, - {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"}, - {file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"}, - {file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"}, - {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"}, - {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"}, - {file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"}, - {file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"}, - {file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"}, - {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.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.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"}, - {file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"}, - {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.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.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"}, - {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.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"}, - {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, + {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.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.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, + {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, + {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.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, + {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, + {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.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, + {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, + {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, + {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, + {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, + {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, + {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]] name = "cachetools" version = "5.3.1" @@ -205,16 +246,93 @@ files = [ {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]] name = "click" -version = "8.1.6" +version = "8.1.7" description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, - {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -275,14 +393,14 @@ graph = ["objgraph (>=1.7.2)"] [[package]] name = "flask" -version = "2.3.2" +version = "2.3.3" description = "A simple framework for building complex web applications." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Flask-2.3.2-py3-none-any.whl", hash = "sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0"}, - {file = "Flask-2.3.2.tar.gz", hash = "sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef"}, + {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, + {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, ] [package.dependencies] @@ -291,7 +409,7 @@ click = ">=8.1.3" importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.1.2" Jinja2 = ">=3.1.2" -Werkzeug = ">=2.3.3" +Werkzeug = ">=2.3.7" [package.extras] async = ["asgiref (>=3.2)"] @@ -330,18 +448,19 @@ Flask = "*" [[package]] name = "flask-compress" -version = "1.13" +version = "1.14" description = "Compress responses in your Flask app with gzip, deflate or brotli." category = "main" optional = false python-versions = "*" files = [ - {file = "Flask-Compress-1.13.tar.gz", hash = "sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d"}, - {file = "Flask_Compress-1.13-py3-none-any.whl", hash = "sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b"}, + {file = "Flask-Compress-1.14.tar.gz", hash = "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee"}, + {file = "Flask_Compress-1.14-py3-none-any.whl", hash = "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c"}, ] [package.dependencies] -brotli = "*" +brotli = {version = "*", markers = "platform_python_implementation != \"PyPy\""} +brotlicffi = {version = "*", markers = "platform_python_implementation == \"PyPy\""} flask = "*" [[package]] @@ -362,14 +481,14 @@ Werkzeug = ">=1.0.1" [[package]] name = "flask-migrate" -version = "4.0.4" +version = "4.0.5" description = "SQLAlchemy database migrations for Flask applications using Alembic." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "Flask-Migrate-4.0.4.tar.gz", hash = "sha256:73293d40b10ac17736e715b377e7b7bde474cb8105165d77474df4c3619b10b3"}, - {file = "Flask_Migrate-4.0.4-py3-none-any.whl", hash = "sha256:77580f27ab39bc68be4906a43c56d7674b45075bc4f883b1d0b985db5164d58f"}, + {file = "Flask-Migrate-4.0.5.tar.gz", hash = "sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1"}, + {file = "Flask_Migrate-4.0.5-py3-none-any.whl", hash = "sha256:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef"}, ] [package.dependencies] @@ -379,19 +498,19 @@ Flask-SQLAlchemy = ">=1.0" [[package]] name = "flask-sqlalchemy" -version = "3.0.5" +version = "3.1.1" description = "Add SQLAlchemy support to your Flask application." category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "flask_sqlalchemy-3.0.5-py3-none-any.whl", hash = "sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283"}, - {file = "flask_sqlalchemy-3.0.5.tar.gz", hash = "sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1"}, + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, ] [package.dependencies] flask = ">=2.2.5" -sqlalchemy = ">=1.4.18" +sqlalchemy = ">=2.0.16" [[package]] name = "greenlet" @@ -469,18 +588,18 @@ test = ["objgraph", "psutil"] [[package]] name = "gunicorn" -version = "20.1.0" +version = "21.2.0" description = "WSGI HTTP Server for UNIX" category = "main" optional = false python-versions = ">=3.5" files = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, ] [package.dependencies] -setuptools = ">=3.0" +packaging = "*" [package.extras] eventlet = ["eventlet (>=0.24.1)"] @@ -510,22 +629,22 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.0.0" +version = "6.1.0" description = "Read resources from Python packages" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.0.0-py3-none-any.whl", hash = "sha256:d952faee11004c045f785bb5636e8f885bed30dc3c940d5d42798a2a4541c185"}, - {file = "importlib_resources-6.0.0.tar.gz", hash = "sha256:4cf94875a8368bd89531a756df9a9ebe1f150e0f885030b461237bc7f2d905f2"}, + {file = "importlib_resources-6.1.0-py3-none-any.whl", hash = "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83"}, + {file = "importlib_resources-6.1.0.tar.gz", hash = "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "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"] +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", "zipp (>=3.17)"] [[package]] name = "isort" @@ -727,6 +846,22 @@ files = [ {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]] name = "mccabe" 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)"] 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]] name = "pylint" -version = "2.17.5" +version = "2.17.6" description = "python code static checker" category = "main" optional = false python-versions = ">=3.7.2" files = [ - {file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"}, - {file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"}, + {file = "pylint-2.17.6-py3-none-any.whl", hash = "sha256:18a1412e873caf8ffb56b760ce1b5643675af23e6173a247b502406b24c716af"}, + {file = "pylint-2.17.6.tar.gz", hash = "sha256:be928cce5c76bf9acdc65ad01447a1e0b1a7bccffc609fb7fc40f2513045bd05"}, ] [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\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, @@ -903,14 +1050,14 @@ testutils = ["gitpython (>3)"] [[package]] 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" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, - {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, ] [package.extras] @@ -967,71 +1114,152 @@ files = [ ] [[package]] -name = "setuptools" -version = "68.0.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +name = "regex" +version = "2023.8.8" +description = "Alternative regular expression module, to replace re." category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.6" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, + {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]] name = "sqlalchemy" -version = "2.0.19" +version = "2.0.21" description = "Database Abstraction Library" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9deaae357edc2091a9ed5d25e9ee8bba98bcfae454b3911adeaf159c2e9ca9e3"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bf0fd65b50a330261ec7fe3d091dfc1c577483c96a9fa1e4323e932961aa1b5"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d90ccc15ba1baa345796a8fb1965223ca7ded2d235ccbef80a47b85cea2d71a"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4e688f6784427e5f9479d1a13617f573de8f7d4aa713ba82813bcd16e259d1"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:584f66e5e1979a7a00f4935015840be627e31ca29ad13f49a6e51e97a3fb8cae"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c69ce70047b801d2aba3e5ff3cba32014558966109fecab0c39d16c18510f15"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-win32.whl", hash = "sha256:96f0463573469579d32ad0c91929548d78314ef95c210a8115346271beeeaaa2"}, - {file = "SQLAlchemy-2.0.19-cp310-cp310-win_amd64.whl", hash = "sha256:22bafb1da60c24514c141a7ff852b52f9f573fb933b1e6b5263f0daa28ce6db9"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6894708eeb81f6d8193e996257223b6bb4041cb05a17cd5cf373ed836ef87a2"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8f2afd1aafded7362b397581772c670f20ea84d0a780b93a1a1529da7c3d369"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15afbf5aa76f2241184c1d3b61af1a72ba31ce4161013d7cb5c4c2fca04fd6e"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc05b59142445a4efb9c1fd75c334b431d35c304b0e33f4fa0ff1ea4890f92e"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5831138f0cc06b43edf5f99541c64adf0ab0d41f9a4471fd63b54ae18399e4de"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3afa8a21a9046917b3a12ffe016ba7ebe7a55a6fc0c7d950beb303c735c3c3ad"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-win32.whl", hash = "sha256:c896d4e6ab2eba2afa1d56be3d0b936c56d4666e789bfc59d6ae76e9fcf46145"}, - {file = "SQLAlchemy-2.0.19-cp311-cp311-win_amd64.whl", hash = "sha256:024d2f67fb3ec697555e48caeb7147cfe2c08065a4f1a52d93c3d44fc8e6ad1c"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:89bc2b374ebee1a02fd2eae6fd0570b5ad897ee514e0f84c5c137c942772aa0c"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4d410a76c3762511ae075d50f379ae09551d92525aa5bb307f8343bf7c2c12"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f469f15068cd8351826df4080ffe4cc6377c5bf7d29b5a07b0e717dddb4c7ea2"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cda283700c984e699e8ef0fcc5c61f00c9d14b6f65a4f2767c97242513fcdd84"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:43699eb3f80920cc39a380c159ae21c8a8924fe071bccb68fc509e099420b148"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-win32.whl", hash = "sha256:61ada5831db36d897e28eb95f0f81814525e0d7927fb51145526c4e63174920b"}, - {file = "SQLAlchemy-2.0.19-cp37-cp37m-win_amd64.whl", hash = "sha256:57d100a421d9ab4874f51285c059003292433c648df6abe6c9c904e5bd5b0828"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:16a310f5bc75a5b2ce7cb656d0e76eb13440b8354f927ff15cbaddd2523ee2d1"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7b5e3856cbf1876da4e9d9715546fa26b6e0ba1a682d5ed2fc3ca4c7c3ec5b"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e7b69d9ced4b53310a87117824b23c509c6fc1f692aa7272d47561347e133b6"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9eb4575bfa5afc4b066528302bf12083da3175f71b64a43a7c0badda2be365"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6b54d1ad7a162857bb7c8ef689049c7cd9eae2f38864fc096d62ae10bc100c7d"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5d6afc41ca0ecf373366fd8e10aee2797128d3ae45eb8467b19da4899bcd1ee0"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-win32.whl", hash = "sha256:430614f18443b58ceb9dedec323ecddc0abb2b34e79d03503b5a7579cd73a531"}, - {file = "SQLAlchemy-2.0.19-cp38-cp38-win_amd64.whl", hash = "sha256:eb60699de43ba1a1f77363f563bb2c652f7748127ba3a774f7cf2c7804aa0d3d"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a752b7a9aceb0ba173955d4f780c64ee15a1a991f1c52d307d6215c6c73b3a4c"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7351c05db355da112e056a7b731253cbeffab9dfdb3be1e895368513c7d70106"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa51ce4aea583b0c6b426f4b0563d3535c1c75986c4373a0987d84d22376585b"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae7473a67cd82a41decfea58c0eac581209a0aa30f8bc9190926fbf628bb17f7"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851a37898a8a39783aab603c7348eb5b20d83c76a14766a43f56e6ad422d1ec8"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539010665c90e60c4a1650afe4ab49ca100c74e6aef882466f1de6471d414be7"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-win32.whl", hash = "sha256:f82c310ddf97b04e1392c33cf9a70909e0ae10a7e2ddc1d64495e3abdc5d19fb"}, - {file = "SQLAlchemy-2.0.19-cp39-cp39-win_amd64.whl", hash = "sha256:8e712cfd2e07b801bc6b60fdf64853bc2bd0af33ca8fa46166a23fe11ce0dbb0"}, - {file = "SQLAlchemy-2.0.19-py3-none-any.whl", hash = "sha256:314145c1389b021a9ad5aa3a18bac6f5d939f9087d7fc5443be28cba19d2c972"}, - {file = "SQLAlchemy-2.0.19.tar.gz", hash = "sha256:77a14fa20264af73ddcdb1e2b9c5a829b8cc6b8304d0f093271980e36c200a3f"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-win32.whl", hash = "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-win_amd64.whl", hash = "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-win32.whl", hash = "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-win_amd64.whl", hash = "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a95aa0672e3065d43c8aa80080cdd5cc40fe92dc873749e6c1cf23914c4b83af"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8c323813963b2503e54d0944813cd479c10c636e3ee223bcbd7bd478bf53c178"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:419b1276b55925b5ac9b4c7044e999f1787c69761a3c9756dec6e5c225ceca01"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-win32.whl", hash = "sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9"}, + {file = "SQLAlchemy-2.0.21-cp37-cp37m-win_amd64.whl", hash = "sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4eae01faee9f2b17f08885e3f047153ae0416648f8e8c8bd9bc677c5ce64be9"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b19ae41ef26c01a987e49e37c77b9ad060c59f94d3b3efdfdbf4f3daaca7b5fe"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:014794b60d2021cc8ae0f91d4d0331fe92691ae5467a00841f7130fe877b678e"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-win32.whl", hash = "sha256:0268256a34806e5d1c8f7ee93277d7ea8cc8ae391f487213139018b6805aeaf6"}, + {file = "SQLAlchemy-2.0.21-cp38-cp38-win_amd64.whl", hash = "sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-win32.whl", hash = "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-win_amd64.whl", hash = "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce"}, + {file = "SQLAlchemy-2.0.21-py3-none-any.whl", hash = "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce"}, + {file = "SQLAlchemy-2.0.21.tar.gz", hash = "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258"}, ] [package.dependencies] @@ -1039,7 +1267,7 @@ greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or typing-extensions = ">=4.2.0" [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)"] asyncio = ["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]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] @@ -1112,14 +1340,14 @@ files = [ [[package]] name = "werkzeug" -version = "2.3.6" +version = "2.3.7" description = "The comprehensive WSGI web application library." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Werkzeug-2.3.6-py3-none-any.whl", hash = "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890"}, - {file = "Werkzeug-2.3.6.tar.gz", hash = "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330"}, + {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"}, + {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"}, ] [package.dependencies] @@ -1215,21 +1443,21 @@ files = [ [[package]] name = "zipp" -version = "3.16.2" +version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, - {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [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"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "96ec0d1f7b512afb05455262fa2de8c4f862bf68fdae513f8552dc30c6e5ab49" +content-hash = "f3ff236ae81d76796722dcd183466f483b6d5aba6dbfcffc145747aa0bb36087" diff --git a/pyproject.toml b/pyproject.toml index 3752e8a..ed1a9d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OnlyLegs" -version = "0.1.5" +version = "0.1.7" repository = "https://github.com/Fluffy-Bean/onlylegs" license = "MIT" readme = "README.md" @@ -16,8 +16,8 @@ Flask-Compress = "^1.13" Flask-Caching = "1.10.1" Flask-Assets = "^2.0" Flask-Login = "^0.6.2" -python-dotenv = "^0.21.0" -gunicorn = "^20.1.0" +python-dotenv = "1.0.0" +gunicorn = "^21.2.0" pyyaml = "^6.0" colorthief = "^0.2.1" Pillow = "^9.4.0" @@ -28,6 +28,7 @@ cssmin = "^0.2.0" pylint = "^2.16.3" black = "^23.3.0" cachetools = "^5.3.0" +material-color-utilities-python = "^0.1.5" [build-system] requires = ["poetry-core"] diff --git a/setup/args.py b/setup/args.py deleted file mode 100644 index a443338..0000000 --- a/setup/args.py +++ /dev/null @@ -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 diff --git a/setup/configuration.py b/setup/configuration.py deleted file mode 100644 index 2727ce0..0000000 --- a/setup/configuration.py +++ /dev/null @@ -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", - ) diff --git a/setup/runner.py b/setup/runner.py deleted file mode 100644 index 94cd827..0000000 --- a/setup/runner.py +++ /dev/null @@ -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")