Add option to collapse metadata

This commit is contained in:
Michał Gdula 2023-01-31 23:44:44 +00:00
parent c02d618844
commit f845f614df
7 changed files with 223 additions and 95 deletions

View file

@ -5,7 +5,7 @@ print("""
| |_| | | | | | |_| | |__| __/ (_| \\__ \\ | |_| | | | | | |_| | |__| __/ (_| \\__ \\
\\___/|_| |_|_|\\__, |_____\\___|\\__, |___/ \\___/|_| |_|_|\\__, |_____\\___|\\__, |___/
|___/ |___/ |___/ |___/
Created by Fluffy Bean - Version 260123 Created by Fluffy Bean - Version 310123
""") """)
from flask import Flask, render_template from flask import Flask, render_template
@ -14,13 +14,10 @@ from dotenv import load_dotenv
import yaml import yaml
import os import os
compress = Compress()
def create_app(test_config=None): def create_app(test_config=None):
# create and configure the app # create and configure the app
app = Flask(__name__) app = Flask(__name__)
compress.init_app(app) compress = Compress()
# Get environment variables # Get environment variables
load_dotenv(os.path.join(app.root_path, 'user', '.env')) load_dotenv(os.path.join(app.root_path, 'user', '.env'))
@ -104,4 +101,5 @@ def create_app(test_config=None):
from . import api from . import api
app.register_blueprint(api.blueprint) app.register_blueprint(api.blueprint)
compress.init_app(app)
return app return app

View file

@ -1,4 +1,4 @@
from flask import Blueprint, render_template, current_app, send_from_directory, send_file, request, g, abort, flash, jsonify from flask import Blueprint, current_app, send_from_directory, send_file, request, g, abort, flash, jsonify
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from gallery.auth import login_required from gallery.auth import login_required
from gallery.db import get_db from gallery.db import get_db
@ -109,6 +109,7 @@ def remove(id):
flash(['Image was all in Le Head!', 1]) flash(['Image was all in Le Head!', 1])
return 'Gwa Gwa' return 'Gwa Gwa'
@blueprint.route('/metadata/<int:id>', methods=['GET']) @blueprint.route('/metadata/<int:id>', methods=['GET'])
def metadata(id): def metadata(id):
img = get_db().execute( img = get_db().execute(
@ -117,7 +118,8 @@ def metadata(id):
if img is None: if img is None:
abort(404) abort(404)
exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], img['file_name'])) exif = mt.metadata.yoink(
os.path.join(current_app.config['UPLOAD_FOLDER'], img['file_name']))
return jsonify(exif) return jsonify(exif)

View file

@ -6,6 +6,7 @@ import os
class metadata: class metadata:
def yoink(filename): def yoink(filename):
exif = metadata.getFile(filename) exif = metadata.getFile(filename)
file_size = os.path.getsize(filename) file_size = os.path.getsize(filename)
@ -13,7 +14,8 @@ class metadata:
file_resolution = Image.open(filename).size file_resolution = Image.open(filename).size
if exif: if exif:
unformatted_exif = metadata.format(exif, file_size, file_name, file_resolution) unformatted_exif = metadata.format(exif, file_size, file_name,
file_resolution)
else: else:
# No EXIF data, get some basic informaton from the file # No EXIF data, get some basic informaton from the file
unformatted_exif = { unformatted_exif = {
@ -43,10 +45,10 @@ class metadata:
} }
formatted_exif = {} formatted_exif = {}
for section in unformatted_exif: for section in unformatted_exif:
tmp = {} tmp = {}
for value in unformatted_exif[section]: for value in unformatted_exif[section]:
if unformatted_exif[section][value]['raw'] != None: if unformatted_exif[section][value]['raw'] != None:
raw_type = unformatted_exif[section][value]['raw'] raw_type = unformatted_exif[section][value]['raw']
@ -54,12 +56,12 @@ class metadata:
raw_type = raw_type.__float__() raw_type = raw_type.__float__()
elif isinstance(raw_type, bytes): elif isinstance(raw_type, bytes):
raw_type = raw_type.decode('utf-8') raw_type = raw_type.decode('utf-8')
tmp[value] = unformatted_exif[section][value] tmp[value] = unformatted_exif[section][value]
if len(tmp) > 0: if len(tmp) > 0:
formatted_exif[section] = tmp formatted_exif[section] = tmp
return formatted_exif return formatted_exif
def getFile(filename): def getFile(filename):
@ -126,14 +128,21 @@ class metadata:
'raw': raw['LensModel']['raw'], 'raw': raw['LensModel']['raw'],
}, },
'Lense Spec': { 'Lense Spec': {
'type': 'text', 'type':
'raw': raw['LensSpecification']['raw'], 'text',
'formatted': metadata.lensSpecification(raw['LensSpecification']['raw']) 'raw':
raw['LensSpecification']['raw'],
'formatted':
metadata.lensSpecification(raw['LensSpecification']['raw'])
}, },
'Component Config': { 'Component Config': {
'type': 'text', 'type':
'raw': raw['ComponentsConfiguration']['raw'], 'text',
'formatted': metadata.componentsConfiguration(raw['ComponentsConfiguration']['raw']) 'raw':
raw['ComponentsConfiguration']['raw'],
'formatted':
metadata.componentsConfiguration(
raw['ComponentsConfiguration']['raw'])
}, },
'Date Processed': { 'Date Processed': {
'type': 'date', 'type': 'date',
@ -175,7 +184,8 @@ class metadata:
'Focal Length (35mm format)': { 'Focal Length (35mm format)': {
'type': 'focal', 'type': 'focal',
'raw': raw["FocalLengthIn35mmFilm"]["raw"], 'raw': raw["FocalLengthIn35mmFilm"]["raw"],
'formatted': metadata.focal(raw["FocalLengthIn35mmFilm"]["raw"]) 'formatted':
metadata.focal(raw["FocalLengthIn35mmFilm"]["raw"])
}, },
'Max Aperture': { 'Max Aperture': {
'type': 'fnumber', 'type': 'fnumber',
@ -203,9 +213,12 @@ class metadata:
'formatted': metadata.iso(raw["ISOSpeed"]["raw"]) 'formatted': metadata.iso(raw["ISOSpeed"]["raw"])
}, },
'Sensitivity Type': { 'Sensitivity Type': {
'type': 'number', 'type':
'raw': raw["SensitivityType"]["raw"], 'number',
'formatted': metadata.sensitivityType(raw["SensitivityType"]["raw"]) 'raw':
raw["SensitivityType"]["raw"],
'formatted':
metadata.sensitivityType(raw["SensitivityType"]["raw"])
}, },
'Exposure Bias': { 'Exposure Bias': {
'type': 'ev', 'type': 'ev',
@ -223,9 +236,12 @@ class metadata:
'formatted': metadata.exposureMode(raw["ExposureMode"]["raw"]) 'formatted': metadata.exposureMode(raw["ExposureMode"]["raw"])
}, },
'Exposure Program': { 'Exposure Program': {
'type': 'number', 'type':
'raw': raw["ExposureProgram"]["raw"], 'number',
'formatted': metadata.exposureProgram(raw["ExposureProgram"]["raw"]) 'raw':
raw["ExposureProgram"]["raw"],
'formatted':
metadata.exposureProgram(raw["ExposureProgram"]["raw"])
}, },
'White Balance': { 'White Balance': {
'type': 'number', 'type': 'number',
@ -248,9 +264,12 @@ class metadata:
'formatted': metadata.lightSource(raw["LightSource"]["raw"]) 'formatted': metadata.lightSource(raw["LightSource"]["raw"])
}, },
'Scene Capture Type': { 'Scene Capture Type': {
'type': 'number', 'type':
'raw': raw["SceneCaptureType"]["raw"], 'number',
'formatted': metadata.sceneCaptureType(raw["SceneCaptureType"]["raw"]) 'raw':
raw["SceneCaptureType"]["raw"],
'formatted':
metadata.sceneCaptureType(raw["SceneCaptureType"]["raw"])
}, },
'Scene Type': { 'Scene Type': {
'type': 'number', 'type': 'number',
@ -265,7 +284,8 @@ class metadata:
'Rating Percent': { 'Rating Percent': {
'type': 'number', 'type': 'number',
'raw': raw["RatingPercent"]["raw"], 'raw': raw["RatingPercent"]["raw"],
'formatted': metadata.ratingPercent(raw["RatingPercent"]["raw"]) 'formatted':
metadata.ratingPercent(raw["RatingPercent"]["raw"])
}, },
} }
exif['Software'] = { exif['Software'] = {
@ -322,7 +342,8 @@ class metadata:
'Resolution Units': { 'Resolution Units': {
'type': 'number', 'type': 'number',
'raw': raw["ResolutionUnit"]["raw"], 'raw': raw["ResolutionUnit"]["raw"],
'formatted': metadata.resolutionUnit(raw["ResolutionUnit"]["raw"]) 'formatted':
metadata.resolutionUnit(raw["ResolutionUnit"]["raw"])
}, },
} }
#exif['Raw'] = {} #exif['Raw'] = {}
@ -339,7 +360,7 @@ class metadata:
# } # }
return exif return exif
def human_size(num, suffix="B"): def human_size(num, suffix="B"):
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0: if abs(num) < 1024.0:
@ -360,19 +381,19 @@ class metadata:
return 'f/' + str(value) return 'f/' + str(value)
else: else:
return None return None
def iso(value): def iso(value):
if value != None: if value != None:
return 'ISO ' + str(value) return 'ISO ' + str(value)
else: else:
return None return None
def shutter(value): def shutter(value):
if value != None: if value != None:
return str(value) + 's' return str(value) + 's'
else: else:
return None return None
def focal(value): def focal(value):
if value != None: if value != None:
try: try:
@ -381,54 +402,72 @@ class metadata:
return str(value) + 'mm' return str(value) + 'mm'
else: else:
return None return None
def ev(value): def ev(value):
if value != None: if value != None:
return str(value) + 'EV' return str(value) + 'EV'
else: else:
return None return None
def colorSpace(value): def colorSpace(value):
types = { types = {1: 'sRGB', 65535: 'Uncalibrated', 0: 'Reserved'}
1: 'sRGB',
65535: 'Uncalibrated',
0: 'Reserved'
}
try: try:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def flash(value): def flash(value):
types = { types = {
0: 'Flash did not fire', 0:
1: 'Flash fired', 'Flash did not fire',
5: 'Strobe return light not detected', 1:
7: 'Strobe return light detected', 'Flash fired',
9: 'Flash fired, compulsory flash mode', 5:
13: 'Flash fired, compulsory flash mode, return light not detected', 'Strobe return light not detected',
15: 'Flash fired, compulsory flash mode, return light detected', 7:
16: 'Flash did not fire, compulsory flash mode', 'Strobe return light detected',
24: 'Flash did not fire, auto mode', 9:
25: 'Flash fired, auto mode', 'Flash fired, compulsory flash mode',
29: 'Flash fired, auto mode, return light not detected', 13:
31: 'Flash fired, auto mode, return light detected', 'Flash fired, compulsory flash mode, return light not detected',
32: 'No flash function', 15:
65: 'Flash fired, red-eye reduction mode', 'Flash fired, compulsory flash mode, return light detected',
69: 'Flash fired, red-eye reduction mode, return light not detected', 16:
71: 'Flash fired, red-eye reduction mode, return light detected', 'Flash did not fire, compulsory flash mode',
73: 'Flash fired, compulsory flash mode, red-eye reduction mode', 24:
77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', 'Flash did not fire, auto mode',
79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', 25:
89: 'Flash fired, auto mode, red-eye reduction mode', 'Flash fired, auto mode',
93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', 29:
95: 'Flash fired, auto mode, return light detected, red-eye reduction mode' 'Flash fired, auto mode, return light not detected',
31:
'Flash fired, auto mode, return light detected',
32:
'No flash function',
65:
'Flash fired, red-eye reduction mode',
69:
'Flash fired, red-eye reduction mode, return light not detected',
71:
'Flash fired, red-eye reduction mode, return light detected',
73:
'Flash fired, compulsory flash mode, red-eye reduction mode',
77:
'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
79:
'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
89:
'Flash fired, auto mode, red-eye reduction mode',
93:
'Flash fired, auto mode, return light not detected, red-eye reduction mode',
95:
'Flash fired, auto mode, return light detected, red-eye reduction mode'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def exposureProgram(value): def exposureProgram(value):
types = { types = {
0: 'Not defined', 0: 'Not defined',
@ -445,7 +484,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def meteringMode(value): def meteringMode(value):
types = { types = {
0: 'Unknown', 0: 'Unknown',
@ -461,7 +500,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def resolutionUnit(value): def resolutionUnit(value):
types = { types = {
1: 'No absolute unit of measurement', 1: 'No absolute unit of measurement',
@ -472,7 +511,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def lightSource(value): def lightSource(value):
types = { types = {
0: 'Unknown', 0: 'Unknown',
@ -501,7 +540,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def sceneCaptureType(value): def sceneCaptureType(value):
types = { types = {
0: 'Standard', 0: 'Standard',
@ -513,13 +552,13 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def sceneType(value): def sceneType(value):
if value: if value:
return 'Directly photographed image' return 'Directly photographed image'
else: else:
return None return None
def whiteBalance(value): def whiteBalance(value):
types = { types = {
0: 'Auto white balance', 0: 'Auto white balance',
@ -529,7 +568,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def exposureMode(value): def exposureMode(value):
types = { types = {
0: 'Auto exposure', 0: 'Auto exposure',
@ -540,29 +579,38 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def sensitivityType(value): def sensitivityType(value):
types = { types = {
0: 'Unknown', 0:
1: 'Standard Output Sensitivity', 'Unknown',
2: 'Recommended Exposure Index', 1:
3: 'ISO Speed', 'Standard Output Sensitivity',
4: 'Standard Output Sensitivity and Recommended Exposure Index', 2:
5: 'Standard Output Sensitivity and ISO Speed', 'Recommended Exposure Index',
6: 'Recommended Exposure Index and ISO Speed', 3:
7: 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', 'ISO Speed',
4:
'Standard Output Sensitivity and Recommended Exposure Index',
5:
'Standard Output Sensitivity and ISO Speed',
6:
'Recommended Exposure Index and ISO Speed',
7:
'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def lensSpecification(value): def lensSpecification(value):
if value: if value:
return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm' return str(value[0] / value[1]) + 'mm - ' + str(
value[2] / value[3]) + 'mm'
else: else:
return None return None
def compression(value): def compression(value):
types = { types = {
1: 'Uncompressed', 1: 'Uncompressed',
@ -619,7 +667,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def orientation(value): def orientation(value):
types = { types = {
1: 'Horizontal (normal)', 1: 'Horizontal (normal)',
@ -635,7 +683,7 @@ class metadata:
return types[int(value)] return types[int(value)]
except: except:
return None return None
def componentsConfiguration(value): def componentsConfiguration(value):
types = { types = {
0: '', 0: '',
@ -650,9 +698,9 @@ class metadata:
return ''.join([types[int(x)] for x in value]) return ''.join([types[int(x)] for x in value])
except: except:
return None return None
def rating(value): def rating(value):
return str(value) + ' stars' return str(value) + ' stars'
def ratingPercent(value): def ratingPercent(value):
return str(value) + '%' return str(value) + '%'

View file

@ -10,6 +10,7 @@ from PIL import Image
import os import os
from datetime import datetime from datetime import datetime
dt = datetime.now() dt = datetime.now()
blueprint = Blueprint('gallery', __name__) blueprint = Blueprint('gallery', __name__)
@ -33,7 +34,8 @@ def image(id):
if image is None: if image is None:
abort(404) abort(404)
exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name'])) exif = mt.metadata.yoink(
os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name']))
return render_template('image.html', image=image, exif=exif) return render_template('image.html', image=image, exif=exif)

View file

@ -76,6 +76,11 @@
<div class="image-info__container"> <div class="image-info__container">
{% if image['alt'] != '' %} {% if image['alt'] != '' %}
<div class="image-info"> <div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
</svg>
</span>
<div class="image-info__header"> <div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2.5 24 24" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2.5 24 24" fill="currentColor">
<path d="M3.656 17.979A1 1 0 0 1 2 17.243V15a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H8.003l-4.347 2.979zm.844-3.093a.536.536 0 0 0 .26-.069l2.355-1.638A1 1 0 0 1 7.686 13H12a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v5c0 .54.429.982 1 1 .41.016.707.083.844.226.128.134.135.36.156.79.003.063.003.177 0 .37a.5.5 0 0 0 .5.5z"></path><path d="M16 10.017a7.136 7.136 0 0 0 0 .369v-.37c.02-.43.028-.656.156-.79.137-.143.434-.21.844-.226.571-.018 1-.46 1-1V3a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1H5V2a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2v2.243a1 1 0 0 1-1.656.736L16 13.743v-3.726z"></path> <path d="M3.656 17.979A1 1 0 0 1 2 17.243V15a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H8.003l-4.347 2.979zm.844-3.093a.536.536 0 0 0 .26-.069l2.355-1.638A1 1 0 0 1 7.686 13H12a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v5c0 .54.429.982 1 1 .41.016.707.083.844.226.128.134.135.36.156.79.003.063.003.177 0 .37a.5.5 0 0 0 .5.5z"></path><path d="M16 10.017a7.136 7.136 0 0 0 0 .369v-.37c.02-.43.028-.656.156-.79.137-.143.434-.21.844-.226.571-.018 1-.46 1-1V3a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1H5V2a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2v2.243a1 1 0 0 1-1.656.736L16 13.743v-3.726z"></path>
@ -89,6 +94,11 @@
{% endif %} {% endif %}
{% if image['description'] != '' %} {% if image['description'] != '' %}
<div class="image-info"> <div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
</svg>
</span>
<div class="image-info__header"> <div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24" fill="currentColor">
<path d="M3 0h10a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm2 1h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 12h2a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"></path> <path d="M3 0h10a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm2 1h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 12h2a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"></path>
@ -101,6 +111,11 @@
</div> </div>
{% endif %} {% endif %}
<div class="image-info"> <div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
</svg>
</span>
<div class="image-info__header"> <div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24" fill="currentColor">
<path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-10a1 1 0 0 1 1 1v5a1 1 0 0 1-2 0V9a1 1 0 0 1 1-1zm0-1a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"></path> <path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-10a1 1 0 0 1 1 1v5a1 1 0 0 1-2 0V9a1 1 0 0 1 1-1zm0-1a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"></path>
@ -126,6 +141,11 @@
</div> </div>
{% for tag in exif %} {% for tag in exif %}
<div class="image-info"> <div class="image-info">
<span class="image-info__collapse" id="collapse-info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
</svg>
</span>
{% if tag == 'Photographer' %} {% if tag == 'Photographer' %}
<div class="image-info__header"> <div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -2 24 24" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -2 24 24" fill="currentColor">
@ -189,6 +209,14 @@
{% block script %} {% block script %}
<script> <script>
var collapseButton = document.querySelectorAll('#collapse-info');
for (var i = 0; i < collapseButton.length; i++) {
collapseButton[i].addEventListener('click', function() {
this.parentNode.classList.toggle('image-info__collapsed');
});
}
$('.image-fullscreen').click(function() { $('.image-fullscreen').click(function() {
$('.image-fullscreen').removeClass('image-fullscreen__active'); $('.image-fullscreen').removeClass('image-fullscreen__active');
}); });

View file

@ -105,9 +105,37 @@
display: flex display: flex
flex-direction: column flex-direction: column
position: relative
background-color: $black background-color: $black
border-radius: $rad border-radius: $rad
.image-info__collapse
margin: 0
padding: 0
width: 1.5rem
height: 1.5rem
display: flex
justify-content: center
align-items: center
position: absolute
top: 0.5rem
right: 0.5rem
cursor: pointer
z-index: +2
transition: transform 0.15s cubic-bezier(.79, .14, .15, .86)
svg
margin: 0
padding: 0
fill: $primary
.image-info__header .image-info__header
margin: 0 margin: 0
padding: 0.5rem padding: 0.5rem
@ -159,6 +187,8 @@
overflow-x: hidden overflow-x: hidden
transition: opacity 0.3s cubic-bezier(.79, .14, .15, .86)
p p
margin: 0 margin: 0
padding: 0 padding: 0
@ -186,8 +216,11 @@
white-space: nowrap white-space: nowrap
td
padding-bottom: 0.5rem
td:first-child td:first-child
padding: 0.25rem 1rem 0.25rem 0 padding-right: 0.5rem
width: 50% width: 50%
max-width: 0 max-width: 0
@ -199,7 +232,7 @@
font-size: 1rem font-size: 1rem
font-weight: 500 font-weight: 500
td:last-child td:last-child
padding: 0.25rem 0 padding: 0
width: 50% width: 50%
max-width: 0 max-width: 0
@ -214,6 +247,19 @@
td.empty-table td.empty-table
opacity: 0.3 opacity: 0.3
tr:last-of-type td
padding-bottom: 0
.image-info__collapsed
height: 2.5rem
.image-info__collapse
transform: rotate(90deg)
.image-info__content
padding: 0
opacity: 0
#image-tools #image-tools
margin-bottom: 0.5rem margin-bottom: 0.5rem
@ -260,6 +306,10 @@
.image-info__header .image-info__header
border-radius: $rad $rad 0 0 border-radius: $rad $rad 0 0
.image-info__collapsed
.image-info__header
border-radius: $rad
@media (max-width: $breakpoint) @media (max-width: $breakpoint)
.image-fullscreen .image-fullscreen
padding: 0 0 3.5rem 0 padding: 0 0 3.5rem 0

View file

@ -2,7 +2,7 @@ from setuptools import find_packages, setup
setup( setup(
name='onlylegs', name='onlylegs',
version='260123', version='310123',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[