Add support for different image directories

This changed the url for images, from file to media
This commit is contained in:
Michał 2023-04-20 20:45:57 +00:00
parent 8e54a3b4b6
commit 87a8254c73
7 changed files with 93 additions and 27 deletions

View file

@ -4,16 +4,16 @@ Onlylegs - API endpoints
from uuid import uuid4
import os
import pathlib
import re
import logging
from flask import Blueprint, send_from_directory, abort, flash, request, current_app
from werkzeug.utils import secure_filename
from flask_login import login_required, current_user
from colorthief import ColorThief
from onlylegs.extensions import db
from onlylegs.models import Post, Group, GroupJunction
from onlylegs.models import Post, Group, GroupJunction, User
from onlylegs.utils import metadata as mt
from onlylegs.utils.generate_image import generate_thumbnail
@ -21,25 +21,24 @@ from onlylegs.utils.generate_image import generate_thumbnail
blueprint = Blueprint("api", __name__, url_prefix="/api")
@blueprint.route("/file/<file_name>", methods=["GET"])
def file(file_name):
@blueprint.route("/media/<path:path>", methods=["GET"])
def media(path):
"""
Returns a file from the uploads folder
r for resolution, 400x400 or thumb for thumbnail
r for resolution, thumb for thumbnail etc
e for extension, jpg, png etc
"""
res = request.args.get("r", default=None, type=str) # Type of file (thumb, etc)
ext = request.args.get("e", default=None, type=str) # File extension
file_name = secure_filename(file_name) # Sanitize file name
res = request.args.get("r", default=None, type=str)
ext = request.args.get("e", default=None, type=str)
# path = secure_filename(path)
# if no args are passed, return the raw file
if not res and not ext:
if not os.path.exists(
os.path.join(current_app.config["UPLOAD_FOLDER"], file_name)
):
if not os.path.exists(os.path.join(current_app.config["MEDIA_FOLDER"], path)):
abort(404)
return send_from_directory(current_app.config["UPLOAD_FOLDER"], file_name)
return send_from_directory(current_app.config["MEDIA_FOLDER"], path)
thumb = generate_thumbnail(file_name, res, ext)
thumb = generate_thumbnail(path, res, ext)
if not thumb:
abort(404)
@ -197,3 +196,69 @@ def delete_group():
flash(["Group yeeted!", "1"])
return ":3"
@blueprint.route("/user/picture/<int:user_id>", methods=["POST"])
def user_picture(user_id):
"""
Returns the profile of a user
"""
user = db.get_or_404(User, user_id)
file = request.files["file"]
# If no image is uploaded, return 404 error
if not file:
return abort(404)
elif user.id != current_user.id:
return abort(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)
abort(403)
if user.picture:
os.remove(os.path.join(current_app.config["PFP_FOLDER"], user.picture))
# Save file
try:
file.save(img_path)
except OSError as err:
logging.info("Error saving file %s because of %s", img_path, err)
abort(500)
img_colors = ColorThief(img_path).get_color() # Get color palette
# Save to database
user.colour = img_colors
user.picture = str(img_name + "." + img_ext)
db.session.commit()
return "Gwa Gwa" # Return something so the browser doesn't show an error
@blueprint.route("/user/username/<int:user_id>", methods=["POST"])
def user_username(user_id):
"""
Returns the profile of a user
"""
user = db.get_or_404(User, 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):
abort(400)
elif user.id != current_user.id:
return abort(403)
# Save to database
user.username = new_name
db.session.commit()
return "Gwa Gwa" # Return something so the browser doesn't show an error

View file

@ -36,6 +36,7 @@ WEBSITE_CONF = conf["website"]
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

View file

@ -209,8 +209,7 @@
.navigation {
background-color: rgb({{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }}) !important;
}
.navigation-item > svg {
fill: {{ text_colour }} !important;
.navigation-item > i {
color: {{ text_colour }} !important;
}
.navigation-item.selected::before {
@ -222,7 +221,7 @@
{% block content %}
{% if images %}
<div class="banner">
<img src="{{ url_for('api.file', file_name=images.0.filename ) }}?r=prev" onload="imgFade(this)" style="opacity:0;" alt="{% if images.0.alt %}{{ images.0.alt }}{% else %}Group Banner{% endif %}"/>
<img src="{{ url_for('api.media', path='uploads/' + images.0.filename ) }}?r=prev" onload="imgFade(this)" style="opacity:0;" alt="{% if images.0.alt %}{{ images.0.alt }}{% else %}Group Banner{% endif %}"/>
<span class="banner-filter"></span>
<div class="banner-content">
<p class="banner-info"><a href="{{ url_for('profile.profile', id=group.author.id) }}" class="link">By {{ group.author.username }}</a></p>
@ -268,7 +267,7 @@
<div class="image-filter">
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
</div>
<img alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}" data-src="{{ url_for('api.file', file_name=image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load"/>
<img alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}" data-src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load"/>
</a>
{% endfor %}
</div>

View file

@ -2,7 +2,7 @@
{% block page_index %}
{% if return_page %}?page={{ return_page }}{% endif %}{% endblock %}
{% block head %}
<meta property="og:image" content="{{ url_for('api.file', file_name=image.filename) }}"/>
<meta property="og:image" content="{{ url_for('api.media', path='uploads/' + image.filename) }}"/>
<meta name="theme-color" content="rgb({{ image.colours.0.0 }}{{ image.colours.0.1 }}{{ image.colours.0.2 }})"/>
<script type="text/javascript">
@ -82,7 +82,7 @@
{% endblock %}
{% block content %}
<div class="background">
<img src="{{ url_for('api.file', file_name=image.filename) }}?r=prev" alt="{{ image.alt }}" onload="imgFade(this)" style="opacity:0;"/>
<img src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev" alt="{{ image.alt }}" onload="imgFade(this)" style="opacity:0;"/>
<span></span>
</div>
@ -90,7 +90,7 @@
<div class="image-block">
<div class="image-container">
<img
src="{{ url_for('api.file', file_name=image.filename) }}?r=prev"
src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev"
alt="{{ image.alt }}"
onload="imgFade(this)"
style="opacity: 0;"
@ -107,7 +107,7 @@
<div>
<button class="pill-item" onclick="fullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
<button class="pill-item" onclick="imageShare()"><i class="ph ph-export"></i></button>
<a class="pill-item" href="{{ url_for('api.file', file_name=image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
<a class="pill-item" href="{{ url_for('api.media', path='uploads/' + image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
</div>
{% if current_user.id == image.author.id %}
<div>

View file

@ -35,7 +35,7 @@
<div class="image-filter">
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
</div>
<img fetchpriority="low" alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}" data-src="{{ url_for('api.file', file_name=image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load"/>
<img fetchpriority="low" alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}" data-src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load"/>
</a>
{% endfor %}
</div>

View file

@ -126,7 +126,7 @@
<div class="images size-{{ group.images|length }}">
{% if group.images|length > 0 %}
{% for image in group.images %}
<img data-src="{{ url_for('api.file', file_name=image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load" class="data-{{ loop.index }}" {% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}/>
<img data-src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb" onload="this.classList.add('loaded');" id="lazy-load" class="data-{{ loop.index }}" {% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}/>
{% endfor %}
{% else %}
<img src="{{ url_for('static', filename='error.png') }}" class="loaded" alt="Error thumbnail"/>

View file

@ -4,11 +4,11 @@ Tools for generating images and thumbnails
import os
from PIL import Image, ImageOps
from onlylegs.config import UPLOAD_FOLDER, CACHE_FOLDER
from onlylegs.config import MEDIA_FOLDER, CACHE_FOLDER
from werkzeug.utils import secure_filename
def generate_thumbnail(file_name, resolution, ext=None):
def generate_thumbnail(file_path, resolution, ext=None):
"""
Image thumbnail generator
Uses PIL to generate a thumbnail of the image and saves it to the cache directory
@ -21,6 +21,7 @@ def generate_thumbnail(file_name, resolution, ext=None):
os.makedirs(CACHE_FOLDER)
# no sussy business
file_name = os.path.basename(file_path)
file_name, file_ext = secure_filename(file_name).rsplit(".")
if not ext:
ext = file_ext.strip(".")
@ -44,11 +45,11 @@ def generate_thumbnail(file_name, resolution, ext=None):
return os.path.join(CACHE_FOLDER, 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_FOLDER, f"{file_name}.{file_ext}")):
if not os.path.exists(os.path.join(MEDIA_FOLDER, file_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_FOLDER, f"{file_name}.{file_ext}"))
image = Image.open(os.path.join(MEDIA_FOLDER, file_path, f"{file_name}.{file_ext}"))
image_icc = image.info.get("icc_profile")
img_x, img_y = image.size