mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2025-01-29 01:28:24 +00:00
Add context menu script
This commit is contained in:
parent
2db4d0a6ea
commit
8a4fe891ef
107
onlylegs/api.py
107
onlylegs/api.py
|
@ -7,6 +7,9 @@ import re
|
|||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from PIL import Image
|
||||
from PIL.ExifTags import TAGS
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
abort,
|
||||
|
@ -20,8 +23,7 @@ from flask_login import login_required, current_user
|
|||
from colorthief import ColorThief
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import Users, Pictures
|
||||
from onlylegs.utils.metadata import yoink
|
||||
from onlylegs.models import Users, Pictures, Exif
|
||||
from onlylegs.utils.generate_image import generate_thumbnail
|
||||
|
||||
|
||||
|
@ -137,46 +139,91 @@ def upload():
|
|||
"""
|
||||
Uploads an image to the server and saves it to the database
|
||||
"""
|
||||
form_file = request.files.get("file", None)
|
||||
form = request.form
|
||||
image_description = request.form.get("description", "").strip()
|
||||
image_alt = request.form.get("alt", "").strip()
|
||||
image_file = request.files.get("file", None)
|
||||
|
||||
if not form_file:
|
||||
if not image_file:
|
||||
return jsonify({"message": "No file"}), 400
|
||||
|
||||
# Get file extension, generate random name and set file path
|
||||
img_ext = pathlib.Path(form_file.filename).suffix.replace(".", "").lower()
|
||||
img_name = "GWAGWA_" + str(uuid4())
|
||||
img_path = os.path.join(
|
||||
current_app.config["UPLOAD_FOLDER"], img_name + "." + img_ext
|
||||
)
|
||||
image_mime = pathlib.Path(image_file.filename).suffix.replace(".", "").lower()
|
||||
image_name = "GWAGWA_" + str(uuid4())
|
||||
image_filename = image_name + "." + image_mime
|
||||
save_path = os.path.join(current_app.config["UPLOAD_FOLDER"], image_filename)
|
||||
|
||||
# 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)
|
||||
if image_mime not in current_app.config["ALLOWED_EXTENSIONS"].keys():
|
||||
logging.info("File extension not allowed: %s", image_mime)
|
||||
return jsonify({"message": "File extension not allowed"}), 403
|
||||
|
||||
# Save file
|
||||
try:
|
||||
form_file.save(img_path)
|
||||
except OSError as err:
|
||||
logging.info("Error saving file %s because of %s", img_path, err)
|
||||
return jsonify({"message": "Error saving file"}), 500
|
||||
image_file.save(save_path)
|
||||
|
||||
img_exif = yoink(img_path) # Get EXIF data
|
||||
img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette
|
||||
image_colours = ColorThief(save_path).get_palette(color_count=3)
|
||||
|
||||
# Save to database
|
||||
query = Pictures(
|
||||
image_record = Pictures(
|
||||
author_id=current_user.id,
|
||||
filename=img_name + "." + img_ext,
|
||||
mimetype=img_ext,
|
||||
exif=img_exif,
|
||||
colours=img_colors,
|
||||
description=form["description"],
|
||||
alt=form["alt"],
|
||||
filename=image_filename,
|
||||
mimetype=image_mime,
|
||||
colours=image_colours,
|
||||
description=image_description,
|
||||
alt=image_alt,
|
||||
)
|
||||
db.session.add(image_record)
|
||||
db.session.commit()
|
||||
|
||||
db.session.add(query)
|
||||
image_exif = []
|
||||
with Image.open(save_path) as file:
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key="FileName",
|
||||
value=image_filename,
|
||||
)
|
||||
)
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key="FileSize",
|
||||
value=os.path.getsize(save_path),
|
||||
)
|
||||
)
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key="FileFormat",
|
||||
value=image_mime,
|
||||
)
|
||||
)
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key="FileWidth",
|
||||
value=file.size[0],
|
||||
)
|
||||
)
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key="FileHeight",
|
||||
value=file.size[1],
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
tags = file._getexif()
|
||||
for tag, value in TAGS.items():
|
||||
if tag in tags:
|
||||
image_exif.append(
|
||||
Exif(
|
||||
picture_id=image_record.id,
|
||||
key=value,
|
||||
value=tags[tag],
|
||||
)
|
||||
)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
db.session.add_all(image_exif)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "File uploaded"}), 200
|
||||
|
|
|
@ -6,7 +6,7 @@ from flask_login import UserMixin
|
|||
from onlylegs.extensions import db
|
||||
|
||||
|
||||
class AlbumJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class AlbumJunction(db.Model):
|
||||
"""
|
||||
Junction table for picturess and albums
|
||||
Joins with picturess and albums
|
||||
|
@ -26,7 +26,7 @@ class AlbumJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
)
|
||||
|
||||
|
||||
class Pictures(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class Pictures(db.Model):
|
||||
"""
|
||||
Pictures table
|
||||
"""
|
||||
|
@ -38,7 +38,6 @@ class Pictures(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
|
||||
filename = db.Column(db.String, unique=True, nullable=False)
|
||||
mimetype = db.Column(db.String, nullable=False)
|
||||
exif = db.Column(db.PickleType, nullable=False)
|
||||
colours = db.Column(db.PickleType, nullable=False)
|
||||
|
||||
description = db.Column(db.String, nullable=False)
|
||||
|
@ -51,9 +50,24 @@ class Pictures(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
)
|
||||
|
||||
album_fk = db.relationship("AlbumJunction", backref="pictures")
|
||||
exif_fk = db.relationship("Exif", backref="pictures")
|
||||
|
||||
|
||||
class Albums(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class Exif(db.Model):
|
||||
"""
|
||||
Exif data for pictures
|
||||
"""
|
||||
|
||||
__tablename__ = "exif"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
picture_id = db.Column(db.Integer, db.ForeignKey("pictures.id"))
|
||||
|
||||
key = db.Column(db.String, nullable=False)
|
||||
value = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class Albums(db.Model):
|
||||
"""
|
||||
albums table
|
||||
"""
|
||||
|
@ -75,7 +89,7 @@ class Albums(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
album_fk = db.relationship("AlbumJunction", backref="albums")
|
||||
|
||||
|
||||
class Users(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103
|
||||
class Users(db.Model, UserMixin):
|
||||
"""
|
||||
Users table
|
||||
"""
|
||||
|
|
135
onlylegs/static/js/contextMenu.js
Normal file
135
onlylegs/static/js/contextMenu.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
function showContextMenu(obj, menu, position='mouse') {
|
||||
// If the context menu is already open, close it first
|
||||
if (document.querySelector(".contextMenu")) {
|
||||
dissmissContextMenu();
|
||||
}
|
||||
|
||||
// Add span to close the context menu
|
||||
let contextCloseSpan = document.createElement("span");
|
||||
contextCloseSpan.className = "contextMenuClose";
|
||||
contextCloseSpan.onclick = dissmissContextMenu;
|
||||
|
||||
// Create the context menu
|
||||
let contextMenu = document.createElement("div");
|
||||
contextMenu.className = "contextMenu";
|
||||
|
||||
// Create the menu items
|
||||
menu.forEach(array => {
|
||||
if (array.value === "divider") {
|
||||
let divider = document.createElement("hr");
|
||||
divider.className = "contextMenuDivider";
|
||||
|
||||
contextMenu.appendChild(divider);
|
||||
return;
|
||||
} else if (array['value'] === "title") {
|
||||
let titleP = document.createElement("p");
|
||||
titleP.className = "contextMenuTitle";
|
||||
titleP.innerHTML = array.text;
|
||||
|
||||
contextMenu.appendChild(titleP);
|
||||
let divider = document.createElement("hr");
|
||||
divider.className = "contextMenuDivider";
|
||||
|
||||
contextMenu.appendChild(divider);
|
||||
return;
|
||||
}
|
||||
|
||||
let itemBtn = document.createElement("button");
|
||||
itemBtn.className = "contextMenuItem";
|
||||
itemBtn.onclick = array['function'];
|
||||
|
||||
if (array['type'] === "critical") {
|
||||
itemBtn.classList.add("contextMenuItem__critical");
|
||||
} else if (array['type'] === "warning") {
|
||||
itemBtn.classList.add("contextMenuItem__warning");
|
||||
} else if (array['type'] === "success") {
|
||||
itemBtn.classList.add("contextMenuItem__success");
|
||||
} else if (array['type'] === "info") {
|
||||
itemBtn.classList.add("contextMenuItem__info");
|
||||
}
|
||||
|
||||
let itemIcon = document.createElement("span");
|
||||
itemIcon.className = "contextMenuIcon";
|
||||
if (array['icon']) {
|
||||
itemIcon.innerHTML = array['icon'];
|
||||
} else {
|
||||
itemIcon.innerHTML = '';
|
||||
}
|
||||
itemBtn.appendChild(itemIcon);
|
||||
|
||||
// Create the text for the action
|
||||
let itemText = document.createElement("p");
|
||||
itemText.className = "contextMenuText";
|
||||
itemText.innerHTML = array.value;
|
||||
itemBtn.appendChild(itemText);
|
||||
|
||||
contextMenu.appendChild(itemBtn);
|
||||
});
|
||||
|
||||
// Add the context menu to the body
|
||||
document.body.appendChild(contextMenu);
|
||||
document.body.appendChild(contextCloseSpan);
|
||||
|
||||
let posX;
|
||||
let posY;
|
||||
|
||||
if (position === 'mouse') {
|
||||
posX = event.clientX + 5;
|
||||
posY = event.clientY + 5;
|
||||
} else if (position === 'button') {
|
||||
posX = obj.offsetLeft + (obj.offsetWidth / 2) - (contextMenu.offsetWidth / 2);
|
||||
posY = obj.offsetTop + obj.offsetHeight + 5;
|
||||
} else if (position === 'center') {
|
||||
posX = (window.innerWidth / 2) - (contextMenu.offsetWidth / 2);
|
||||
posY = (window.innerHeight / 2) - (contextMenu.offsetHeight / 2);
|
||||
} else {
|
||||
posX = event.clientX + 5;
|
||||
posY = event.clientY + 5;
|
||||
}
|
||||
|
||||
// Move the context menu if it is off the screen
|
||||
if (posX + contextMenu.offsetWidth > window.innerWidth) {
|
||||
posX = window.innerWidth - (contextMenu.offsetWidth + 5);
|
||||
} else if (posX < 0) {
|
||||
posX = 5;
|
||||
}
|
||||
if (posY < 0) {
|
||||
posY = 5;
|
||||
}
|
||||
contextMenu.style.left = posX + "px";
|
||||
contextMenu.style.top = posY + "px";
|
||||
|
||||
// Timeout otherwise animation doesn't work
|
||||
setTimeout(function() {
|
||||
if (position === 'mouse') {
|
||||
contextMenu.classList.add("contextMenu__show--mouse");
|
||||
} else if (position === 'button') {
|
||||
contextMenu.classList.add("contextMenu__show--button");
|
||||
} else if (position === 'center') {
|
||||
contextMenu.classList.add("contextMenu__show--center");
|
||||
} else {
|
||||
contextMenu.classList.add("contextMenu__show");
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
function dissmissContextMenu() {
|
||||
// Remove the close span
|
||||
let contextSpan = document.querySelectorAll(".contextMenuClose");
|
||||
contextSpan.forEach(menu => {
|
||||
menu.remove();
|
||||
});
|
||||
|
||||
// Get the context menu
|
||||
let contextMenu = document.querySelectorAll(".contextMenu");
|
||||
contextMenu.forEach(menu => {
|
||||
menu.classList.add("contextMenu__hide");
|
||||
setTimeout(function() {
|
||||
menu.remove();
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
window.onresize = () => {
|
||||
dissmissContextMenu();
|
||||
}
|
|
@ -12,6 +12,69 @@ function imageFullscreen() {
|
|||
document.cookie = "image-info=1"
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
'function': () => {
|
||||
dissmissContextMenu();
|
||||
imageEditPopup();
|
||||
},
|
||||
'type': 'critical'
|
||||
},
|
||||
{
|
||||
'value': 'Delete',
|
||||
'function': () => {
|
||||
dissmissContextMenu();
|
||||
imageDeletePopup();
|
||||
},
|
||||
'type': 'critical'
|
||||
}
|
||||
], 'button')
|
||||
}
|
||||
|
||||
function imageDeletePopup() {
|
||||
let title = 'DESTRUCTION!!!!!!';
|
||||
let subtitle =
|
||||
|
@ -92,7 +155,6 @@ function imageEditConfirm(event) {
|
|||
body: form,
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
popupDismiss();
|
||||
window.location.reload();
|
||||
} else {
|
||||
addNotification('Image *clings*', 2);
|
||||
|
|
|
@ -9,13 +9,17 @@ function popupShow(titleText, subtitleText, bodyContent=null, userActions=null)
|
|||
actionsSelector.innerHTML = '';
|
||||
|
||||
// Set popup header and subtitle
|
||||
let titleElement = document.createElement('h2');
|
||||
titleElement.innerHTML = titleText;
|
||||
headerSelector.appendChild(titleElement);
|
||||
if (titleText) {
|
||||
let titleElement = document.createElement('h2');
|
||||
titleElement.innerHTML = titleText;
|
||||
headerSelector.appendChild(titleElement);
|
||||
}
|
||||
|
||||
let subtitleElement = document.createElement('p');
|
||||
subtitleElement.innerHTML = subtitleText;
|
||||
headerSelector.appendChild(subtitleElement);
|
||||
if (subtitleText) {
|
||||
let subtitleElement = document.createElement('p');
|
||||
subtitleElement.innerHTML = subtitleText;
|
||||
headerSelector.appendChild(subtitleElement);
|
||||
}
|
||||
|
||||
if (bodyContent) { headerSelector.appendChild(bodyContent) }
|
||||
|
||||
|
|
128
onlylegs/static/sass/components/context-menu.sass
Normal file
128
onlylegs/static/sass/components/context-menu.sass
Normal file
|
@ -0,0 +1,128 @@
|
|||
.contextMenuClose
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
background-color: transparent
|
||||
z-index: 99998
|
||||
|
||||
.contextMenu
|
||||
margin: 0
|
||||
padding: 0.25rem
|
||||
|
||||
width: calc( 100vw - 10px )
|
||||
height: auto
|
||||
max-width: 300px
|
||||
|
||||
position: absolute
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
gap: 0.25rem
|
||||
|
||||
background-color: RGB($bg-300)
|
||||
border: 1px solid RGB($bg-200)
|
||||
border-radius: 6px
|
||||
|
||||
overflow: hidden
|
||||
|
||||
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out
|
||||
transform-origin: center center
|
||||
opacity: 0.5
|
||||
transform: scale(0, 0)
|
||||
z-index: 99999
|
||||
|
||||
.contextMenuTitle
|
||||
margin: 0
|
||||
padding: 0.25rem 0.5rem
|
||||
|
||||
width: 100%
|
||||
|
||||
text-align: center
|
||||
font-size: 1.2rem
|
||||
font-weight: 400
|
||||
color: RGB($fg-white)
|
||||
|
||||
.contextMenuItem
|
||||
margin: 0
|
||||
padding: 0.5rem
|
||||
|
||||
width: 100%
|
||||
height: auto
|
||||
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: flex-start
|
||||
align-items: center
|
||||
gap: 0.5rem
|
||||
|
||||
background-color: RGB($bg-300)
|
||||
color: RGB($fg-white)
|
||||
border: none
|
||||
border-radius: 3px
|
||||
|
||||
cursor: pointer
|
||||
.contextMenuItem:hover
|
||||
background-color: RGB($bg-200)
|
||||
.contextMenuItem__critical
|
||||
color: RGB($critical)
|
||||
.contextMenuItem__warning
|
||||
color: RGB($warning)
|
||||
.contextMenuItem__success
|
||||
color: RGB($success)
|
||||
.contextMenuItem__info
|
||||
color: RGB($primary)
|
||||
|
||||
.contextMenuText
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 400
|
||||
|
||||
.contextMenuIcon
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
width: 1.25rem
|
||||
height: 1.25rem
|
||||
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
.contextMenuDivider
|
||||
margin: 0 auto
|
||||
padding: 0
|
||||
|
||||
width: 100%
|
||||
height: 1px
|
||||
|
||||
border: none
|
||||
background-color: RGB($bg-200)
|
||||
|
||||
.contextMenu__show
|
||||
opacity: 1
|
||||
transform: scale(1, 1)
|
||||
|
||||
.contextMenu__show--mouse
|
||||
opacity: 1
|
||||
transform: scale(1, 1)
|
||||
transform-origin: top left
|
||||
.contextMenu__show--button
|
||||
opacity: 1
|
||||
transform: scale(1, 1)
|
||||
transform-origin: top center
|
||||
.contextMenu__show--center
|
||||
opacity: 1
|
||||
transform: scale(1, 1)
|
||||
transform-origin: center center
|
||||
|
||||
.contextMenu__hide
|
||||
opacity: 0
|
||||
transform: scale(0, 0)
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
@import "components/notification"
|
||||
@import "components/pop-up"
|
||||
@import "components/context-menu"
|
||||
@import "components/upload-panel"
|
||||
@import "components/tags"
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
'id': {{ image.id }},
|
||||
'description': '{{ image.description }}',
|
||||
'alt': '{{ image.alt }}',
|
||||
};
|
||||
'filename': '{{ image.filename }}',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -31,16 +32,12 @@
|
|||
<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()" id="fullscreenImage"><i class="ph ph-info"></i></button>
|
||||
<button class="pill-item" onclick="imageFullscreen()"><i class="ph ph-info"></i></button>
|
||||
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
|
||||
<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>
|
||||
{% 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>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if current_user.id == image.author.id %}
|
||||
<div>
|
||||
<button class="pill-item pill__critical" onclick="imageDeletePopup()"><i class="ph ph-trash"></i></button>
|
||||
<button class="pill-item pill__critical" onclick="imageEditPopup()"><i class="ph ph-pencil-simple"></i></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,42 +113,20 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
</details>
|
||||
{% for tag in image.exif %}
|
||||
<details open>
|
||||
<summary>
|
||||
{% if tag == 'Photographer' %}
|
||||
<i class="ph ph-person"></i><h2>Photographer</h2>
|
||||
{% elif tag == 'Camera' %}
|
||||
<i class="ph ph-camera"></i><h2>Camera</h2>
|
||||
{% elif tag == 'Software' %}
|
||||
<i class="ph ph-desktop-tower"></i><h2>Software</h2>
|
||||
{% elif tag == 'File' %}
|
||||
<i class="ph ph-file-image"></i><h2>File</h2>
|
||||
{% else %}
|
||||
<i class="ph ph-file-image"></i><h2>{{ tag }}</h2>
|
||||
{% endif %}
|
||||
<span style="width: 100%"></span>
|
||||
<i class="ph ph-caret-down collapse-indicator"></i>
|
||||
</summary>
|
||||
<table>
|
||||
{% for subtag in image.exif[tag] %}
|
||||
<tr>
|
||||
<td>{{ subtag }}</td>
|
||||
{% if image.exif[tag][subtag]['formatted'] %}
|
||||
{% if image.exif[tag][subtag]['type'] == 'date' %}
|
||||
<td><span class="time">{{ image.exif[tag][subtag]['formatted'] }}</span></td>
|
||||
{% else %}
|
||||
<td>{{ image.exif[tag][subtag]['formatted'] }}</td>
|
||||
{% endif %}
|
||||
{% elif image.exif[tag][subtag]['raw'] %}
|
||||
<td>{{ image.exif[tag][subtag]['raw'] }}</td>
|
||||
{% else %}
|
||||
<td class="empty-table">Oops, an error</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</details>
|
||||
{% endfor %}
|
||||
<details open>
|
||||
<summary>
|
||||
<i class="ph ph-file-image"></i><h2>Metadata</h2>
|
||||
<span style="width: 100%"></span>
|
||||
<i class="ph ph-caret-down collapse-indicator"></i>
|
||||
</summary>
|
||||
<table>
|
||||
{% for tag in image_exif %}
|
||||
<tr>
|
||||
<td>{{ tag.key }}</td>
|
||||
<td>{{ tag.value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</details>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,7 +15,7 @@ from flask import (
|
|||
jsonify,
|
||||
)
|
||||
from flask_login import current_user
|
||||
from onlylegs.models import Pictures, AlbumJunction, Albums
|
||||
from onlylegs.models import Pictures, AlbumJunction, Albums, Exif
|
||||
from onlylegs.extensions import db
|
||||
|
||||
|
||||
|
@ -28,7 +28,8 @@ def image(image_id):
|
|||
Image view, shows the image and its metadata
|
||||
"""
|
||||
# Get the image, if it doesn't exist, 404
|
||||
image = db.get_or_404(Pictures, image_id, description="Image not found :<")
|
||||
image_record = db.get_or_404(Pictures, image_id, description="Image not found :<")
|
||||
image_record_exif = Exif.query.filter(Exif.picture_id == image_id).all()
|
||||
|
||||
# Get all groups the image is in
|
||||
groups = (
|
||||
|
@ -38,9 +39,9 @@ def image(image_id):
|
|||
)
|
||||
|
||||
# Get the group data for each group the image is in
|
||||
image.groups = []
|
||||
image_record.groups = []
|
||||
for group in groups:
|
||||
image.groups.append(
|
||||
image_record.groups.append(
|
||||
Albums.query.with_entities(Albums.id, Albums.name)
|
||||
.filter(Albums.id == group[0])
|
||||
.first()
|
||||
|
@ -72,9 +73,8 @@ def image(image_id):
|
|||
limit = current_app.config["UPLOAD_CONF"]["max-load"]
|
||||
|
||||
# If the number of items is less than the limit, no point of calculating the page
|
||||
if len(total_images) <= limit:
|
||||
return_page = None
|
||||
else:
|
||||
return_page = None
|
||||
if len(total_images) > limit:
|
||||
# How many pages should there be
|
||||
for i in range(ceil(len(total_images) / limit)):
|
||||
# Slice the list of IDs into chunks of the limit
|
||||
|
@ -90,7 +90,8 @@ def image(image_id):
|
|||
|
||||
return render_template(
|
||||
"image.html",
|
||||
image=image,
|
||||
image=image_record,
|
||||
image_exif=image_record_exif,
|
||||
next_url=next_url,
|
||||
prev_url=prev_url,
|
||||
return_page=return_page,
|
||||
|
|
Loading…
Reference in a new issue