mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2024-12-28 02:16:07 +00:00
Add support for different image directories
This changed the url for images, from file to media
This commit is contained in:
parent
8e54a3b4b6
commit
87a8254c73
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue