From 3331edf24d9d3d4872f3c6c9520fe877c6846d2a Mon Sep 17 00:00:00 2001 From: Fluffy-Bean Date: Sun, 26 Mar 2023 23:34:03 +0000 Subject: [PATCH] Remove Thumbnails table Daddy PyLint not happy with me --- gallery/__init__.py | 13 +- gallery/db.py | 14 -- gallery/routes/api.py | 53 ++------ gallery/routes/groups.py | 11 +- .../components/image-view/background.sass | 8 +- gallery/utils/contrast.py | 22 +-- gallery/utils/generate_image.py | 127 +++++++++--------- 7 files changed, 104 insertions(+), 144 deletions(-) diff --git a/gallery/__init__.py b/gallery/__init__.py index a869622..259f7b8 100644 --- a/gallery/__init__.py +++ b/gallery/__init__.py @@ -69,14 +69,13 @@ def create_app(test_config=None): # Error handlers @app.errorhandler(Exception) def error_page(err): - # If the error is a HTTP error, return the error page - if isinstance(err, HTTPException): - error = err.code - msg = err.description - return render_template('error.html', error=error, msg=msg), err.code + # If the error is not an HTTPException, return a 500 error + if not isinstance(err, HTTPException): + abort(500) - # Otherwise this an internal error - abort(500) + error = err.code + msg = err.description + return render_template('error.html', error=error, msg=msg), err.code # Load login, registration and logout manager from gallery import auth diff --git a/gallery/db.py b/gallery/db.py index ef00ff3..64b243e 100644 --- a/gallery/db.py +++ b/gallery/db.py @@ -60,20 +60,6 @@ class Posts (base): # pylint: disable=too-few-public-methods, C0103 post_alt = Column(String, nullable=False) junction = relationship('GroupJunction', backref='posts') - thumbnail = relationship('Thumbnails', backref='posts') - - -class Thumbnails (base): # pylint: disable=too-few-public-methods, C0103 - """ - Thumbnail table - """ - __tablename__ = 'thumbnails' - - id = Column(Integer, primary_key=True) - file_name = Column(String, unique=True, nullable=False) - file_ext = Column(String, nullable=False) - resolution = Column(PickleType, nullable=False) - post_id = Column(Integer, ForeignKey('posts.id')) class Groups (base): # pylint: disable=too-few-public-methods, C0103 diff --git a/gallery/routes/api.py b/gallery/routes/api.py index 7677487..dbe9dca 100644 --- a/gallery/routes/api.py +++ b/gallery/routes/api.py @@ -4,9 +4,9 @@ Onlylegs - API endpoints from uuid import uuid4 import os import pathlib -import platformdirs import logging from datetime import datetime as dt +import platformdirs from flask import Blueprint, send_from_directory, abort, flash, jsonify, request, g, current_app from werkzeug.utils import secure_filename @@ -18,7 +18,7 @@ from gallery.auth import login_required from gallery import db from gallery.utils import metadata as mt -from gallery.utils.generate_image import ImageGenerator +from gallery.utils.generate_image import generate_thumbnail blueprint = Blueprint('api', __name__, url_prefix='/api') @@ -41,12 +41,12 @@ def file(file_name): abort(404) return send_from_directory(current_app.config['UPLOAD_FOLDER'], file_name) - - thumb = ImageGenerator.thumbnail(file_name, res) - + + thumb = generate_thumbnail(file_name, res) + if not thumb: abort(404) - + return send_from_directory(os.path.dirname(thumb), os.path.basename(thumb)) @@ -126,8 +126,8 @@ def delete_image(image_id): # Delete cached files cache_path = os.path.join(platformdirs.user_config_dir('onlylegs'), 'cache') cache_name = img.file_name.rsplit('.')[0] - for file in pathlib.Path(cache_path).glob(cache_name + '*'): - os.remove(file) + for cache_file in pathlib.Path(cache_path).glob(cache_name + '*'): + os.remove(cache_file) # Delete from database db_session.query(db.Posts).filter_by(id=image_id).delete() @@ -209,40 +209,3 @@ def metadata(img_id): exif = mt.Metadata(img_path).yoink() return jsonify(exif) - - -@blueprint.route('/logfile') -@login_required -def logfile(): - """ - Gets the log file and returns it as a JSON object - """ - log_dict = {} - - with open('only.log', encoding='utf-8', mode='r') as file: - for i, line in enumerate(file): - line = line.split(' : ') - - event = line[0].strip().split(' ') - event_data = { - 'date': event[0], - 'time': event[1], - 'severity': event[2], - 'owner': event[3] - } - - message = line[1].strip() - try: - message_data = { - 'code': int(message[1:4]), - 'message': message[5:].strip() - } - except ValueError: - message_data = {'code': 0, 'message': message} - except Exception as err: - logging.error('Could not parse log file: %s', err) - abort(500) - - log_dict[i] = {'event': event_data, 'message': message_data} - - return jsonify(log_dict) diff --git a/gallery/routes/groups.py b/gallery/routes/groups.py index 961c9a8..f28690c 100644 --- a/gallery/routes/groups.py +++ b/gallery/routes/groups.py @@ -60,13 +60,18 @@ def group(group_id): for image in group_images: image = db_session.query(db.Posts).filter(db.Posts.id == image[0]).first() images.append(image) - + if images: - text_colour = contrast.contrast(images[0].image_colours[0], 'rgb(var(--fg-black))', 'rgb(var(--fg-white))') + text_colour = contrast.contrast(images[0].image_colours[0], + 'rgb(var(--fg-black))', + 'rgb(var(--fg-white))') else: text_colour = 'rgb(var(--fg-black))' - return render_template('groups/group.html', group=group_item, images=images, text_colour=text_colour) + return render_template('groups/group.html', + group=group_item, + images=images, + text_colour=text_colour) @blueprint.route('//') diff --git a/gallery/themes/default/components/image-view/background.sass b/gallery/themes/default/components/image-view/background.sass index 9d32286..f157561 100644 --- a/gallery/themes/default/components/image-view/background.sass +++ b/gallery/themes/default/components/image-view/background.sass @@ -6,10 +6,10 @@ top: 0 left: 0 - background-color: RGB($fg-white) - // background-image: linear-gradient(to right, RGB($bg-200) 15%, RGB($bg-400) 35%, RGB($bg-200) 50%) - // background-size: 1000px 640px - // animation: imgLoading 1.8s linear infinite forwards + background-color: RGB($bg-300) + background-image: linear-gradient(to right, RGB($bg-400) 15%, RGB($bg-200) 35%, RGB($bg-400) 50%) + background-size: 1000px 640px + animation: imgLoading 1.8s linear infinite forwards user-select: none overflow: hidden diff --git a/gallery/utils/contrast.py b/gallery/utils/contrast.py index abc9e57..27b5d9b 100644 --- a/gallery/utils/contrast.py +++ b/gallery/utils/contrast.py @@ -1,18 +1,22 @@ +""" +Calculate the contrast between two colors +""" + + def contrast(background, light, dark, threshold = 0.179): """ - Calculate the contrast between two colors background: tuple of (r, g, b) values light: color to use if the background is light dark: color to use if the background is dark threshold: the threshold to use for determining lightness, the default is w3 recommended """ - r = background[0] - g = background[1] - b = background[2] + red = background[0] + green = background[1] + blue = background[2] # Calculate contrast - uicolors = [r / 255, g / 255, b / 255] - c = [col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4 for col in uicolors] - l = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]) - - return light if l > threshold else dark + uicolors = [red / 255, green / 255, blue / 255] + cont = [col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4 for col in uicolors] + lightness = (0.2126 * cont[0]) + (0.7152 * cont[1]) + (0.0722 * cont[2]) + + return light if lightness > threshold else dark diff --git a/gallery/utils/generate_image.py b/gallery/utils/generate_image.py index 9a7d9f0..2b5bbd7 100644 --- a/gallery/utils/generate_image.py +++ b/gallery/utils/generate_image.py @@ -1,3 +1,7 @@ +""" +Tools for generating images and thumbnails +""" + import os import platformdirs from PIL import Image, ImageOps #, ImageFilter @@ -8,67 +12,66 @@ CACHE_PATH = platformdirs.user_config_dir('onlylegs') + '/cache' UPLOAD_PATH = platformdirs.user_config_dir('onlylegs') + '/uploads' -class ImageGenerator: - def thumbnail(name, resolution, ext=None): - """ - Image thumbnail generator - Uses PIL to generate a thumbnail of the image and saves it to the cache directory - Name is the filename - resolution: 400x400 or thumb, or any other resolution - ext is the file extension of the image - """ - # Make image cache directory if it doesn't exist - if not os.path.exists(CACHE_PATH): - os.makedirs(CACHE_PATH) +def generate_thumbnail(file_name, resolution, ext=None): + """ + Image thumbnail generator + Uses PIL to generate a thumbnail of the image and saves it to the cache directory + Name is the filename + resolution: 400x400 or thumb, or any other resolution + ext is the file extension of the image + """ + # Make image cache directory if it doesn't exist + if not os.path.exists(CACHE_PATH): + os.makedirs(CACHE_PATH) - # no sussy business - name, name_ext = secure_filename(name).rsplit('.') - if not ext: - ext = name_ext.strip('.') - - # PIL doesnt like jpg so we convert it to jpeg - if ext.lower() == "jpg": - ext = "jpeg" - - # Set resolution based on preset resolutions - if resolution in ['thumb', 'thumbnail']: - res_x, res_y = (400, 400) - elif resolution in ['prev', 'preview']: - res_x, res_y = (1920, 1080) - elif len(resolution.split('x')) == 2: - res_x, res_y = resolution.split('x') - else: - return None - - # If image has been already generated, return it from the cache - if os.path.exists(os.path.join(CACHE_PATH, f'{name}_{res_x}x{res_y}.{ext}')): - return os.path.join(CACHE_PATH, f'{name}_{res_x}x{res_y}.{ext}') - - # Check if image exists in the uploads directory - if not os.path.exists(os.path.join(UPLOAD_PATH, f'{name}.{name_ext}')): - return None - - # Open image and rotate it based on EXIF data and get ICC profile so colors are correct - image = Image.open(os.path.join(UPLOAD_PATH, f'{name}.{name_ext}')) - image_icc = image.info.get("icc_profile") - img_x, img_y = image.size - - # Resize image to fit the resolution - image = ImageOps.exif_transpose(image) - image.thumbnail((min(img_x, int(res_x)), min(img_y, int(res_y))), Image.ANTIALIAS) + # no sussy business + file_name, file_ext = secure_filename(file_name).rsplit('.') + if not ext: + ext = file_ext.strip('.') - # Save image to cache directory - try: - image.save(os.path.join(CACHE_PATH,f'{name}_{res_x}x{res_y}.{ext}'), - icc_profile=image_icc) - except OSError: - # This usually happens when saving a JPEG with an ICC profile, - # so we convert to RGB and try again - image = image.convert('RGB') - image.save(os.path.join(CACHE_PATH, f'{name}_{res_x}x{res_y}.{ext}'), - icc_profile=image_icc) - - # No need to keep the image in memory, learned the hard way - image.close() - - return os.path.join(CACHE_PATH, f'{name}_{res_x}x{res_y}.{ext}') + # PIL doesnt like jpg so we convert it to jpeg + if ext.lower() == "jpg": + ext = "jpeg" + + # Set resolution based on preset resolutions + if resolution in ['thumb', 'thumbnail']: + res_x, res_y = (400, 400) + elif resolution in ['prev', 'preview']: + res_x, res_y = (1920, 1080) + elif len(resolution.split('x')) == 2: + res_x, res_y = resolution.split('x') + else: + return None + + # If image has been already generated, return it from the cache + if os.path.exists(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')): + return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}') + + # Check if image exists in the uploads directory + if not os.path.exists(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}')): + return None + + # Open image and rotate it based on EXIF data and get ICC profile so colors are correct + image = Image.open(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}')) + image_icc = image.info.get("icc_profile") + img_x, img_y = image.size + + # Resize image to fit the resolution + image = ImageOps.exif_transpose(image) + image.thumbnail((min(img_x, int(res_x)), min(img_y, int(res_y))), Image.ANTIALIAS) + + # Save image to cache directory + try: + image.save(os.path.join(CACHE_PATH,f'{file_name}_{res_x}x{res_y}.{ext}'), + icc_profile=image_icc) + except OSError: + # This usually happens when saving a JPEG with an ICC profile, + # so we convert to RGB and try again + image = image.convert('RGB') + image.save(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}'), + icc_profile=image_icc) + + # No need to keep the image in memory, learned the hard way + image.close() + + return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')