mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2024-12-29 10:56:10 +00:00
Add ALT text to images
Correct static methods Tidy up code
This commit is contained in:
parent
e192554a0b
commit
3ee287d6e3
|
@ -5,7 +5,7 @@
|
||||||
| |_| | | | | | |_| | |__| __/ (_| \__ \
|
| |_| | | | | | |_| | |__| __/ (_| \__ \
|
||||||
\___/|_| |_|_|\__, |_____\___|\__, |___/
|
\___/|_| |_|_|\__, |_____\___|\__, |___/
|
||||||
|___/ |___/
|
|___/ |___/
|
||||||
Created by Fluffy Bean - Version 23.03.10
|
Created by Fluffy Bean - Version 23.03.11
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Import system modules
|
# Import system modules
|
||||||
|
@ -46,7 +46,7 @@ def create_app(test_config=None):
|
||||||
"""
|
"""
|
||||||
Create and configure the main app
|
Create and configure the main app
|
||||||
"""
|
"""
|
||||||
app = Flask(__name__,instance_path=os.path.join(USER_DIR, 'instance'))
|
app = Flask(__name__, instance_path=os.path.join(USER_DIR, 'instance'))
|
||||||
assets = Environment()
|
assets = Environment()
|
||||||
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 300})
|
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 300})
|
||||||
compress = Compress()
|
compress = Compress()
|
||||||
|
@ -71,13 +71,11 @@ def create_app(test_config=None):
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Load theme
|
# Load theme
|
||||||
theme_manager.CompileTheme('default', app.root_path)
|
theme_manager.CompileTheme('default', app.root_path)
|
||||||
# Bundle JS files
|
# Bundle JS files
|
||||||
js = Bundle('js/*.js', output='gen/packed.js')
|
js = Bundle('js/*.js', output='gen/packed.js')
|
||||||
assets.register('js_all', js)
|
assets.register('js_all', js)
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(403)
|
@app.errorhandler(403)
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
|
@ -89,7 +87,6 @@ def create_app(test_config=None):
|
||||||
msg = err.description
|
msg = err.description
|
||||||
return render_template('error.html', error=error, msg=msg), err.code
|
return render_template('error.html', error=error, msg=msg), err.code
|
||||||
|
|
||||||
|
|
||||||
# Load login, registration and logout manager
|
# Load login, registration and logout manager
|
||||||
from . import auth
|
from . import auth
|
||||||
app.register_blueprint(auth.blueprint)
|
app.register_blueprint(auth.blueprint)
|
||||||
|
@ -110,10 +107,8 @@ def create_app(test_config=None):
|
||||||
# Load APIs
|
# Load APIs
|
||||||
from . import api
|
from . import api
|
||||||
app.register_blueprint(api.blueprint)
|
app.register_blueprint(api.blueprint)
|
||||||
|
|
||||||
|
|
||||||
logging.info('Gallery started successfully!')
|
|
||||||
|
|
||||||
|
logging.info('Gallery started successfully!')
|
||||||
|
|
||||||
assets.init_app(app)
|
assets.init_app(app)
|
||||||
cache.init_app(app)
|
cache.init_app(app)
|
||||||
|
|
|
@ -19,7 +19,7 @@ from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
from gallery.auth import login_required
|
from gallery.auth import login_required
|
||||||
|
|
||||||
from . import db # Import db to create a session
|
from . import db
|
||||||
from . import metadata as mt
|
from . import metadata as mt
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,15 +39,15 @@ def uploads(file):
|
||||||
# Get args
|
# Get args
|
||||||
width = request.args.get('w', default=0, type=int) # Width of image
|
width = request.args.get('w', default=0, type=int) # Width of image
|
||||||
height = request.args.get('h', default=0, type=int) # Height of image
|
height = request.args.get('h', default=0, type=int) # Height of image
|
||||||
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters
|
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters
|
||||||
blur = request.args.get('b', default=False, type=bool) # Whether to force blur
|
blur = request.args.get('b', default=False, type=bool) # Whether to force blur
|
||||||
|
|
||||||
# if no args are passed, return the raw file
|
# if no args are passed, return the raw file
|
||||||
if width == 0 and height == 0 and not filtered:
|
if width == 0 and height == 0 and not filtered:
|
||||||
if not os.path.exists(os.path.join(current_app.config['UPLOAD_FOLDER'],
|
if not os.path.exists(os.path.join(current_app.config['UPLOAD_FOLDER'],
|
||||||
secure_filename(file))):
|
secure_filename(file))):
|
||||||
abort(404)
|
abort(404)
|
||||||
return send_from_directory(current_app.config['UPLOAD_FOLDER'], file ,as_attachment=True)
|
return send_from_directory(current_app.config['UPLOAD_FOLDER'], file, as_attachment=True)
|
||||||
|
|
||||||
# Of either width or height is 0, set it to the other value to keep aspect ratio
|
# Of either width or height is 0, set it to the other value to keep aspect ratio
|
||||||
if width > 0 and height == 0:
|
if width > 0 and height == 0:
|
||||||
|
@ -60,7 +60,7 @@ def uploads(file):
|
||||||
|
|
||||||
# Open image and set extension
|
# Open image and set extension
|
||||||
try:
|
try:
|
||||||
img = Image.open(os.path.join(current_app.config['UPLOAD_FOLDER'],file))
|
img = Image.open(os.path.join(current_app.config['UPLOAD_FOLDER'], file))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logging.error('File not found: %s, possibly broken upload', file)
|
logging.error('File not found: %s, possibly broken upload', file)
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -79,7 +79,7 @@ def uploads(file):
|
||||||
|
|
||||||
# If has NSFW tag, blur image, etc.
|
# If has NSFW tag, blur image, etc.
|
||||||
if filtered:
|
if filtered:
|
||||||
#img = img.filter(ImageFilter.GaussianBlur(20))
|
# img = img.filter(ImageFilter.GaussianBlur(20))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# If forced to blur, blur image
|
# If forced to blur, blur image
|
||||||
|
@ -122,7 +122,7 @@ def upload():
|
||||||
img_name = "GWAGWA_"+str(uuid4())
|
img_name = "GWAGWA_"+str(uuid4())
|
||||||
img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img_name+'.'+img_ext)
|
img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img_name+'.'+img_ext)
|
||||||
|
|
||||||
if not img_ext in current_app.config['ALLOWED_EXTENSIONS'].keys():
|
if img_ext not in current_app.config['ALLOWED_EXTENSIONS'].keys():
|
||||||
logging.info('File extension not allowed: %s', img_ext)
|
logging.info('File extension not allowed: %s', img_ext)
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
@ -142,14 +142,14 @@ def upload():
|
||||||
|
|
||||||
# Save to database
|
# Save to database
|
||||||
try:
|
try:
|
||||||
query = db.Posts(author_id = g.user.id,
|
query = db.Posts(author_id=g.user.id,
|
||||||
created_at = dt.utcnow(),
|
created_at=dt.utcnow(),
|
||||||
file_name = img_name+'.'+img_ext,
|
file_name=img_name+'.'+img_ext,
|
||||||
file_type = img_ext,
|
file_type=img_ext,
|
||||||
image_exif = img_exif,
|
image_exif=img_exif,
|
||||||
image_colours = img_colors,
|
image_colours=img_colors,
|
||||||
post_description = form_description,
|
post_description=form_description,
|
||||||
post_alt = form_alt)
|
post_alt=form_alt)
|
||||||
|
|
||||||
db_session.add(query)
|
db_session.add(query)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
@ -159,6 +159,7 @@ def upload():
|
||||||
|
|
||||||
return 'Gwa Gwa'
|
return 'Gwa Gwa'
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/delete/<int:image_id>', methods=['POST'])
|
@blueprint.route('/delete/<int:image_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def delete_image(image_id):
|
def delete_image(image_id):
|
||||||
|
@ -194,7 +195,7 @@ def delete_image(image_id):
|
||||||
abort(500)
|
abort(500)
|
||||||
|
|
||||||
logging.info('Removed image (%s) %s', image_id, img.file_name)
|
logging.info('Removed image (%s) %s', image_id, img.file_name)
|
||||||
flash(['Image was all in Le Head!', 1])
|
flash(['Image was all in Le Head!', 0])
|
||||||
return 'Gwa Gwa'
|
return 'Gwa Gwa'
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,7 +269,7 @@ def logfile():
|
||||||
"""
|
"""
|
||||||
Gets the log file and returns it as a JSON object
|
Gets the log file and returns it as a JSON object
|
||||||
"""
|
"""
|
||||||
filename = logging.getLoggerClass().root.handlers[0].baseFilename
|
filename = 'only.log'
|
||||||
log_dict = {}
|
log_dict = {}
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
OnlyLegs - Authentification
|
OnlyLegs - Authentication
|
||||||
User registration, login and logout and locking access to pages behind a login
|
User registration, login and logout and locking access to pages behind a login
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
@ -29,7 +29,7 @@ def login_required(view):
|
||||||
@functools.wraps(view)
|
@functools.wraps(view)
|
||||||
def wrapped_view(**kwargs):
|
def wrapped_view(**kwargs):
|
||||||
if g.user is None or session.get('uuid') is None:
|
if g.user is None or session.get('uuid') is None:
|
||||||
logging.error('Authentification failed')
|
logging.error('Authentication failed')
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect(url_for('gallery.index'))
|
return redirect(url_for('gallery.index'))
|
||||||
|
|
||||||
|
@ -75,7 +75,6 @@ def register():
|
||||||
email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
|
email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
|
||||||
username_regex = re.compile(r'\b[A-Za-z0-9._%+-]+\b')
|
username_regex = re.compile(r'\b[A-Za-z0-9._%+-]+\b')
|
||||||
|
|
||||||
|
|
||||||
if not username or not username_regex.match(username):
|
if not username or not username_regex.match(username):
|
||||||
error.append('Username is invalid!')
|
error.append('Username is invalid!')
|
||||||
|
|
||||||
|
@ -95,7 +94,6 @@ def register():
|
||||||
if error:
|
if error:
|
||||||
return jsonify(error)
|
return jsonify(error)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
register_user = db.Users(username=username,
|
register_user = db.Users(username=username,
|
||||||
email=email,
|
email=email,
|
||||||
|
@ -124,7 +122,6 @@ def login():
|
||||||
user = db_session.query(db.Users).filter_by(username=username).first()
|
user = db_session.query(db.Users).filter_by(username=username).first()
|
||||||
error = []
|
error = []
|
||||||
|
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
logging.error('User %s does not exist. Login attempt from %s',
|
logging.error('User %s does not exist. Login attempt from %s',
|
||||||
username, request.remote_addr)
|
username, request.remote_addr)
|
||||||
|
@ -137,18 +134,17 @@ def login():
|
||||||
if error:
|
if error:
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session.clear()
|
session.clear()
|
||||||
session['user_id'] = user.id
|
session['user_id'] = user.id
|
||||||
session['uuid'] = str(uuid.uuid4())
|
session['uuid'] = str(uuid.uuid4())
|
||||||
|
|
||||||
session_query = db.Sessions(user_id=user.id,
|
session_query = db.Sessions(user_id=user.id,
|
||||||
session_uuid=session.get('uuid'),
|
session_uuid=session.get('uuid'),
|
||||||
ip_address=request.remote_addr,
|
ip_address=request.remote_addr,
|
||||||
user_agent=request.user_agent.string,
|
user_agent=request.user_agent.string,
|
||||||
active=True,
|
active=True,
|
||||||
created_at=dt.utcnow())
|
created_at=dt.utcnow())
|
||||||
|
|
||||||
db_session.add(session_query)
|
db_session.add(session_query)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
OnlyLegs - Database
|
OnlyLegs - Database models and functions for SQLAlchemy
|
||||||
Database models and functions for SQLAlchemy
|
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import platformdirs
|
import platformdirs
|
||||||
|
@ -11,12 +10,12 @@ from sqlalchemy.orm import declarative_base, relationship, backref, mapped_colum
|
||||||
|
|
||||||
path_to_db = os.path.join(platformdirs.user_config_dir('onlylegs'), 'gallery.sqlite')
|
path_to_db = os.path.join(platformdirs.user_config_dir('onlylegs'), 'gallery.sqlite')
|
||||||
engine = create_engine(f'sqlite:///{path_to_db}', echo=False)
|
engine = create_engine(f'sqlite:///{path_to_db}', echo=False)
|
||||||
# engine = create_engine(f'postgresql://username:password@host:port/database_name', echo=False)
|
# engine = create_engine('postgresql://username:password@host:port/database_name', echo=False)
|
||||||
# engine = create_engine(f'mysql://username:password@host:port/database_name', echo=False)
|
# engine = create_engine('mysql://username:password@host:port/database_name', echo=False)
|
||||||
base = declarative_base()
|
base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
class Users (base): # pylint: disable=too-few-public-methods, C0103
|
class Users (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
User table
|
User table
|
||||||
Joins with post, groups, session and log
|
Joins with post, groups, session and log
|
||||||
|
@ -35,7 +34,7 @@ class Users (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
log = relationship('Logs', backref='users')
|
log = relationship('Logs', backref='users')
|
||||||
|
|
||||||
|
|
||||||
class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Post table
|
Post table
|
||||||
Joins with group_junction
|
Joins with group_junction
|
||||||
|
@ -58,7 +57,7 @@ class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
junction = relationship('GroupJunction', backref='posts')
|
junction = relationship('GroupJunction', backref='posts')
|
||||||
|
|
||||||
|
|
||||||
class Groups (base): # pylint: disable=too-few-public-methods, C0103
|
class Groups (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Group table
|
Group table
|
||||||
Joins with group_junction
|
Joins with group_junction
|
||||||
|
@ -74,7 +73,7 @@ class Groups (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
junction = relationship('GroupJunction', backref='groups')
|
junction = relationship('GroupJunction', backref='groups')
|
||||||
|
|
||||||
|
|
||||||
class GroupJunction (base): # pylint: disable=too-few-public-methods, C0103
|
class GroupJunction (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Junction table for posts and groups
|
Junction table for posts and groups
|
||||||
Joins with posts and groups
|
Joins with posts and groups
|
||||||
|
@ -87,7 +86,7 @@ class GroupJunction (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
post_id = Column(Integer, ForeignKey('posts.id'))
|
post_id = Column(Integer, ForeignKey('posts.id'))
|
||||||
|
|
||||||
|
|
||||||
class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Session table
|
Session table
|
||||||
Joins with user
|
Joins with user
|
||||||
|
@ -103,7 +102,7 @@ class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
created_at = Column(DateTime, nullable=False)
|
created_at = Column(DateTime, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Logs (base): # pylint: disable=too-few-public-methods, C0103
|
class Logs (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Log table
|
Log table
|
||||||
Joins with user
|
Joins with user
|
||||||
|
@ -118,7 +117,7 @@ class Logs (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
created_at = Column(DateTime, nullable=False)
|
created_at = Column(DateTime, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Bans (base): # pylint: disable=too-few-public-methods, C0103
|
class Bans (base): # pylint: disable=too-few-public-methods, C0103
|
||||||
"""
|
"""
|
||||||
Bans table
|
Bans table
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Onlylegs - API endpoints
|
Onlylegs - Image Groups
|
||||||
Used intermally by the frontend and possibly by other applications
|
Why groups? Because I don't like calling these albums, sounds more limiting that it actually is
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
@ -31,8 +31,10 @@ def groups():
|
||||||
thumbnail = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group.id).order_by(db.GroupJunction.date_added.desc()).first()
|
thumbnail = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group.id).order_by(db.GroupJunction.date_added.desc()).first()
|
||||||
|
|
||||||
if thumbnail is not None:
|
if thumbnail is not None:
|
||||||
thumbnail_filename = db_session.query(db.Posts.file_name).filter(db.Posts.id == thumbnail[0]).first()
|
group.thumbnail = db_session.query(db.Posts.file_name,
|
||||||
group.thumbnail = thumbnail_filename[0]
|
db.Posts.post_alt,
|
||||||
|
db.Posts.image_colours,
|
||||||
|
db.Posts.id).filter(db.Posts.id == thumbnail[0]).first()
|
||||||
else:
|
else:
|
||||||
group.thumbnail = None
|
group.thumbnail = None
|
||||||
|
|
||||||
|
@ -47,6 +49,8 @@ def group(group_id):
|
||||||
|
|
||||||
if group is None:
|
if group is None:
|
||||||
abort(404, 'Group not found')
|
abort(404, 'Group not found')
|
||||||
|
|
||||||
|
group.author_username = db_session.query(db.Users.username).filter(db.Users.id == group.author_id).first()[0]
|
||||||
|
|
||||||
group_images = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).order_by(db.GroupJunction.date_added.desc()).all()
|
group_images = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).order_by(db.GroupJunction.date_added.desc()).all()
|
||||||
|
|
||||||
|
@ -75,15 +79,12 @@ def group_post(group_id, image_id):
|
||||||
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
||||||
img.groups.append(group)
|
img.groups.append(group)
|
||||||
|
|
||||||
next = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).filter(db.GroupJunction.post_id > image_id).order_by(db.GroupJunction.date_added.asc()).first()
|
next_url = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).filter(db.GroupJunction.post_id > image_id).order_by(db.GroupJunction.date_added.asc()).first()
|
||||||
prev = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).filter(db.GroupJunction.post_id < image_id).order_by(db.GroupJunction.date_added.desc()).first()
|
prev_url = db_session.query(db.GroupJunction.post_id).filter(db.GroupJunction.group_id == group_id).filter(db.GroupJunction.post_id < image_id).order_by(db.GroupJunction.date_added.desc()).first()
|
||||||
|
|
||||||
if next is not None:
|
if next_url is not None:
|
||||||
next = url_for('group.group_post', group_id=group_id, image_id=next[0])
|
next_url = url_for('group.group_post', group_id=group_id, image_id=next_url[0])
|
||||||
if prev is not None:
|
if prev_url is not None:
|
||||||
prev = url_for('group.group_post', group_id=group_id, image_id=prev[0])
|
prev_url = url_for('group.group_post', group_id=group_id, image_id=prev_url[0])
|
||||||
|
|
||||||
return render_template('image.html',
|
return render_template('image.html', image=img, next_url=next_url, prev_url=prev_url)
|
||||||
image=img,
|
|
||||||
next_url=next,
|
|
||||||
prev_url=prev)
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
OnlyLegs - Metatada Parser
|
OnlyLegs - Metadata Parser
|
||||||
Parse metadata from images if available
|
Parse metadata from images if available
|
||||||
otherwise get some basic information from the file
|
otherwise get some basic information from the file
|
||||||
"""
|
"""
|
||||||
|
@ -11,6 +11,7 @@ from PIL.ExifTags import TAGS
|
||||||
from .helpers import *
|
from .helpers import *
|
||||||
from .mapping import *
|
from .mapping import *
|
||||||
|
|
||||||
|
|
||||||
class Metadata:
|
class Metadata:
|
||||||
"""
|
"""
|
||||||
Metadata parser
|
Metadata parser
|
||||||
|
@ -53,7 +54,8 @@ class Metadata:
|
||||||
return None
|
return None
|
||||||
return self.format_data(self.encoded)
|
return self.format_data(self.encoded)
|
||||||
|
|
||||||
def format_data(self, encoded_exif): # pylint: disable=R0912 # For now, this is fine
|
@staticmethod
|
||||||
|
def format_data(encoded_exif):
|
||||||
"""
|
"""
|
||||||
Formats the data into a dictionary
|
Formats the data into a dictionary
|
||||||
"""
|
"""
|
||||||
|
@ -66,15 +68,15 @@ class Metadata:
|
||||||
|
|
||||||
# Thanks chatGPT xP
|
# Thanks chatGPT xP
|
||||||
for key, value in encoded_exif.items():
|
for key, value in encoded_exif.items():
|
||||||
for mapping_name, mapping in EXIF_MAPPING:
|
for mapping_name, mapping_val in EXIF_MAPPING:
|
||||||
if key in mapping:
|
if key in mapping_val:
|
||||||
if len(mapping[key]) == 2:
|
if len(mapping_val[key]) == 2:
|
||||||
exif[mapping_name][mapping[key][0]] = {
|
exif[mapping_name][mapping_val[key][0]] = {
|
||||||
'raw': value,
|
'raw': value,
|
||||||
'formatted': getattr(helpers, mapping[key][1])(value),
|
'formatted': getattr(helpers, mapping_val[key][1])(value),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
exif[mapping_name][mapping[key][0]] = {
|
exif[mapping_name][mapping_val[key][0]] = {
|
||||||
'raw': value,
|
'raw': value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ Metadata formatting helpers
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
def human_size(value):
|
def human_size(value):
|
||||||
"""
|
"""
|
||||||
Formats the size of a file in a human readable format
|
Formats the size of a file in a human readable format
|
||||||
|
@ -276,7 +277,10 @@ def lens_specification(value):
|
||||||
"""
|
"""
|
||||||
Maps the value of the lens specification to a human readable format
|
Maps the value of the lens specification to a human readable format
|
||||||
"""
|
"""
|
||||||
return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm'
|
try:
|
||||||
|
return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm'
|
||||||
|
except Exception as err:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def compression_type(value):
|
def compression_type(value):
|
||||||
|
|
|
@ -61,4 +61,9 @@ FILE_MAPPING = {
|
||||||
'RatingPercent': ['Rating Percent', 'rating_percent'],
|
'RatingPercent': ['Rating Percent', 'rating_percent'],
|
||||||
}
|
}
|
||||||
|
|
||||||
EXIF_MAPPING = [('Photographer', PHOTOGRAHER_MAPPING),('Camera', CAMERA_MAPPING),('Software', SOFTWARE_MAPPING),('File', FILE_MAPPING)]
|
EXIF_MAPPING = [
|
||||||
|
('Photographer', PHOTOGRAHER_MAPPING),
|
||||||
|
('Camera', CAMERA_MAPPING),
|
||||||
|
('Software', SOFTWARE_MAPPING),
|
||||||
|
('File', FILE_MAPPING)
|
||||||
|
]
|
||||||
|
|
|
@ -20,15 +20,17 @@ db_session = db_session()
|
||||||
@blueprint.route('/')
|
@blueprint.route('/')
|
||||||
def index():
|
def index():
|
||||||
"""
|
"""
|
||||||
Home page of the website, shows the feed of latest images
|
Home page of the website, shows the feed of the latest images
|
||||||
"""
|
"""
|
||||||
images = db_session.query(db.Posts.file_name,
|
images = db_session.query(db.Posts.file_name,
|
||||||
|
db.Posts.post_alt,
|
||||||
db.Posts.image_colours,
|
db.Posts.image_colours,
|
||||||
db.Posts.created_at,
|
db.Posts.created_at,
|
||||||
db.Posts.id).order_by(db.Posts.id.desc()).all()
|
db.Posts.id).order_by(db.Posts.id.desc()).all()
|
||||||
|
|
||||||
return render_template('index.html', images=images)
|
return render_template('index.html', images=images)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/image/<int:image_id>')
|
@blueprint.route('/image/<int:image_id>')
|
||||||
def image(image_id):
|
def image(image_id):
|
||||||
"""
|
"""
|
||||||
|
@ -47,15 +49,16 @@ def image(image_id):
|
||||||
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
||||||
img.groups.append(group)
|
img.groups.append(group)
|
||||||
|
|
||||||
next = db_session.query(db.Posts.id).filter(db.Posts.id > image_id).order_by(db.Posts.id.asc()).first()
|
next_url = db_session.query(db.Posts.id).filter(db.Posts.id > image_id).order_by(db.Posts.id.asc()).first()
|
||||||
prev = db_session.query(db.Posts.id).filter(db.Posts.id < image_id).order_by(db.Posts.id.desc()).first()
|
prev_url = db_session.query(db.Posts.id).filter(db.Posts.id < image_id).order_by(db.Posts.id.desc()).first()
|
||||||
|
|
||||||
if next is not None:
|
if next_url is not None:
|
||||||
next = url_for('gallery.image', image_id=next[0])
|
next_url = url_for('gallery.image', image_id=next_url[0])
|
||||||
if prev is not None:
|
if prev_url is not None:
|
||||||
prev = url_for('gallery.image', image_id=prev[0])
|
prev_url = url_for('gallery.image', image_id=prev_url[0])
|
||||||
|
|
||||||
|
return render_template('image.html', image=img, next_url=next_url, prev_url=prev_url)
|
||||||
|
|
||||||
return render_template('image.html', image=img, next_url=next, prev_url=prev)
|
|
||||||
|
|
||||||
@blueprint.route('/profile')
|
@blueprint.route('/profile')
|
||||||
def profile():
|
def profile():
|
||||||
|
@ -64,9 +67,10 @@ def profile():
|
||||||
"""
|
"""
|
||||||
return render_template('profile.html', user_id='gwa gwa')
|
return render_template('profile.html', user_id='gwa gwa')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/profile/<int:user_id>')
|
@blueprint.route('/profile/<int:user_id>')
|
||||||
def profile_id(user_id):
|
def profile_id(user_id):
|
||||||
"""
|
"""
|
||||||
Shows user ofa given id, displays their uploads and other info
|
Shows user ofa given id, displays their uploads and other info
|
||||||
"""
|
"""
|
||||||
return render_template('profile.html', user_id=user_id)
|
return render_template('profile.html', user_id=user_id)
|
||||||
|
|
|
@ -17,6 +17,7 @@ def general():
|
||||||
"""
|
"""
|
||||||
return render_template('settings/general.html')
|
return render_template('settings/general.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/server')
|
@blueprint.route('/server')
|
||||||
@login_required
|
@login_required
|
||||||
def server():
|
def server():
|
||||||
|
@ -25,6 +26,7 @@ def server():
|
||||||
"""
|
"""
|
||||||
return render_template('settings/server.html')
|
return render_template('settings/server.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/account')
|
@blueprint.route('/account')
|
||||||
@login_required
|
@login_required
|
||||||
def account():
|
def account():
|
||||||
|
@ -33,6 +35,7 @@ def account():
|
||||||
"""
|
"""
|
||||||
return render_template('settings/account.html')
|
return render_template('settings/account.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/logs')
|
@blueprint.route('/logs')
|
||||||
@login_required
|
@login_required
|
||||||
def logs():
|
def logs():
|
||||||
|
|
|
@ -8,8 +8,10 @@ import platformdirs
|
||||||
import logging
|
import logging
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
USER_DIR = platformdirs.user_config_dir('onlylegs')
|
USER_DIR = platformdirs.user_config_dir('onlylegs')
|
||||||
|
|
||||||
|
|
||||||
class SetupApp:
|
class SetupApp:
|
||||||
"""
|
"""
|
||||||
Setup the application on first run
|
Setup the application on first run
|
||||||
|
@ -36,7 +38,8 @@ class SetupApp:
|
||||||
print("You can find the config files at:", USER_DIR)
|
print("You can find the config files at:", USER_DIR)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
def make_dir(self):
|
@staticmethod
|
||||||
|
def make_dir():
|
||||||
"""
|
"""
|
||||||
Create the user directory
|
Create the user directory
|
||||||
"""
|
"""
|
||||||
|
@ -49,7 +52,8 @@ class SetupApp:
|
||||||
print("Error creating user directory:", err)
|
print("Error creating user directory:", err)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def make_env(self):
|
@staticmethod
|
||||||
|
def make_env():
|
||||||
"""
|
"""
|
||||||
Create the .env file with default values
|
Create the .env file with default values
|
||||||
"""
|
"""
|
||||||
|
@ -67,7 +71,8 @@ class SetupApp:
|
||||||
|
|
||||||
print("Generated default .env file, please edit!")
|
print("Generated default .env file, please edit!")
|
||||||
|
|
||||||
def make_yaml(self):
|
@staticmethod
|
||||||
|
def make_yaml():
|
||||||
"""
|
"""
|
||||||
Create the YAML config file with default values
|
Create the YAML config file with default values
|
||||||
"""
|
"""
|
||||||
|
@ -107,17 +112,18 @@ class SetupApp:
|
||||||
|
|
||||||
print("Generated default YAML config, please edit!")
|
print("Generated default YAML config, please edit!")
|
||||||
|
|
||||||
def logging_config(self):
|
@staticmethod
|
||||||
LOGS_PATH = os.path.join(platformdirs.user_config_dir('onlylegs'), 'logs')
|
def logging_config():
|
||||||
|
logs_path = os.path.join(platformdirs.user_config_dir('onlylegs'), 'logs')
|
||||||
|
|
||||||
if not os.path.isdir(LOGS_PATH):
|
if not os.path.isdir(logs_path):
|
||||||
os.mkdir(LOGS_PATH)
|
os.mkdir(logs_path)
|
||||||
print("Created logs directory at:", LOGS_PATH)
|
print("Created logs directory at:", logs_path)
|
||||||
|
|
||||||
logging.getLogger('werkzeug').disabled = True
|
logging.getLogger('werkzeug').disabled = True
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
filename=os.path.join(LOGS_PATH, 'only.log'),
|
filename=os.path.join(logs_path, 'only.log'),
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
datefmt='%Y-%m-%d %H:%M:%S',
|
datefmt='%Y-%m-%d %H:%M:%S',
|
||||||
format='%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s',
|
format='%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s',
|
||||||
encoding='utf-8')
|
encoding='utf-8')
|
||||||
|
|
121
gallery/static/js/login.js
Normal file
121
gallery/static/js/login.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// Function to show login
|
||||||
|
function showLogin() {
|
||||||
|
popUpShow(
|
||||||
|
'idk what to put here, just login please',
|
||||||
|
'Need an account? <span class="pop-up__link" onclick="showRegister()">Register!</span>',
|
||||||
|
'<button class="pop-up__btn pop-up__btn-primary-fill" form="loginForm" type="submit">Login</button>',
|
||||||
|
'<form id="loginForm" onsubmit="return login(event)">\
|
||||||
|
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
|
||||||
|
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
|
||||||
|
</form>'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Function to login
|
||||||
|
function login(event) {
|
||||||
|
// AJAX takes control of subby form :3
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let formUsername = document.querySelector("#username").value;
|
||||||
|
let formPassword = document.querySelector("#password").value;
|
||||||
|
|
||||||
|
if (formUsername === "" || formPassword === "") {
|
||||||
|
addNotification("Please fill in all fields!!!!", 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make form
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("username", formUsername);
|
||||||
|
formData.append("password", formPassword);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/auth/login',
|
||||||
|
type: 'post',
|
||||||
|
data: formData,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
success: function (response) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
error: function (response) {
|
||||||
|
switch (response.status) {
|
||||||
|
case 500:
|
||||||
|
addNotification('Server exploded, F\'s in chat', 2);
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
addNotification('None but devils play past here... Wrong information', 2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
addNotification('Error logging in, blame someone', 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Function to show register
|
||||||
|
function showRegister() {
|
||||||
|
popUpShow(
|
||||||
|
'Who are you?',
|
||||||
|
'Already have an account? <span class="pop-up__link" onclick="showLogin()">Login!</span>',
|
||||||
|
'<button class="pop-up__btn pop-up__btn-primary-fill" form="registerForm" type="submit">Register</button>',
|
||||||
|
'<form id="registerForm" onsubmit="return register(event)">\
|
||||||
|
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
|
||||||
|
<input class="pop-up__input" type="text" placeholder="E mail!" id="email"/>\
|
||||||
|
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
|
||||||
|
<input class="pop-up__input" type="password" placeholder="Passywassy again!" id="password-repeat"/>\
|
||||||
|
</form>'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Function to register
|
||||||
|
function register(obj) {
|
||||||
|
// AJAX takes control of subby form
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let formUsername = document.querySelector("#username").value;
|
||||||
|
let formEmail = document.querySelector("#email").value;
|
||||||
|
let formPassword = document.querySelector("#password").value;
|
||||||
|
let formPasswordRepeat = document.querySelector("#password-repeat").value;
|
||||||
|
|
||||||
|
if (formUsername === "" || formEmail === "" || formPassword === "" || formPasswordRepeat === "") {
|
||||||
|
addNotification("Please fill in all fields!!!!", 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make form
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("username", formUsername);
|
||||||
|
formData.append("email", formEmail);
|
||||||
|
formData.append("password", formPassword);
|
||||||
|
formData.append("password-repeat", formPasswordRepeat);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/auth/register',
|
||||||
|
type: 'post',
|
||||||
|
data: formData,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
success: function (response) {
|
||||||
|
if (response === "gwa gwa") {
|
||||||
|
addNotification('Registered successfully! Now please login to continue', 1);
|
||||||
|
showLogin();
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < response.length; i++) {
|
||||||
|
addNotification(response[i], 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (response) {
|
||||||
|
switch (response.status) {
|
||||||
|
case 500:
|
||||||
|
addNotification('Server exploded, F\'s in chat', 2);
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
addNotification('None but devils play past here...', 2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
addNotification('Error logging in, blame someone', 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -40,252 +40,6 @@ function loadOnView() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Function to upload images
|
|
||||||
function uploadFile() {
|
|
||||||
// AJAX takes control of subby form
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const jobList = document.querySelector(".upload-jobs");
|
|
||||||
|
|
||||||
// Check for empty upload
|
|
||||||
if ($("#file").val() === "") {
|
|
||||||
addNotification("Please select a file to upload", 2);
|
|
||||||
} else {
|
|
||||||
// Make form
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("file", $("#file").prop("files")[0]);
|
|
||||||
formData.append("alt", $("#alt").val());
|
|
||||||
formData.append("description", $("#description").val());
|
|
||||||
formData.append("tags", $("#tags").val());
|
|
||||||
formData.append("submit", $("#submit").val());
|
|
||||||
|
|
||||||
// Upload the information
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/upload',
|
|
||||||
type: 'post',
|
|
||||||
data: formData,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
beforeSend: function () {
|
|
||||||
jobContainer = document.createElement("div");
|
|
||||||
jobContainer.classList.add("job");
|
|
||||||
|
|
||||||
jobStatus = document.createElement("span");
|
|
||||||
jobStatus.classList.add("job__status");
|
|
||||||
jobStatus.innerHTML = "Uploading...";
|
|
||||||
|
|
||||||
jobProgress = document.createElement("span");
|
|
||||||
jobProgress.classList.add("progress");
|
|
||||||
|
|
||||||
jobImg = document.createElement("img");
|
|
||||||
jobImg.src = URL.createObjectURL($("#file").prop("files")[0]);
|
|
||||||
|
|
||||||
jobImgFilter = document.createElement("span");
|
|
||||||
jobImgFilter.classList.add("img-filter");
|
|
||||||
|
|
||||||
jobContainer.appendChild(jobStatus);
|
|
||||||
jobContainer.appendChild(jobProgress);
|
|
||||||
jobContainer.appendChild(jobImg);
|
|
||||||
jobContainer.appendChild(jobImgFilter);
|
|
||||||
jobList.appendChild(jobContainer);
|
|
||||||
},
|
|
||||||
success: function (response) {
|
|
||||||
jobContainer.classList.add("success");
|
|
||||||
jobStatus.innerHTML = "Uploaded!";
|
|
||||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
|
||||||
addNotification("Image uploaded successfully", 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
jobContainer.classList.add("critical");
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
jobStatus.innerHTML = "Server exploded, F's in chat";
|
|
||||||
break;
|
|
||||||
case 400:
|
|
||||||
case 404:
|
|
||||||
jobStatus.innerHTML = "Error uploading. Blame yourself";
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
jobStatus.innerHTML = "None but devils play past here...";
|
|
||||||
break;
|
|
||||||
case 413:
|
|
||||||
jobStatus.innerHTML = "File too large!!!!!!";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
jobStatus.innerHTML = "Error uploading file, blame someone";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
|
||||||
addNotification("Error uploading file", 2);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Empty values
|
|
||||||
$("#file").val("");
|
|
||||||
$("#alt").val("");
|
|
||||||
$("#description").val("");
|
|
||||||
$("#tags").val("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// open upload tab
|
|
||||||
function openUploadTab() {
|
|
||||||
// Stop scrolling
|
|
||||||
document.querySelector("html").style.overflow = "hidden";
|
|
||||||
document.querySelector(".content").tabIndex = "-1";
|
|
||||||
|
|
||||||
// Open upload tab
|
|
||||||
const uploadTab = document.querySelector(".upload-panel");
|
|
||||||
uploadTab.style.display = "block";
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
uploadTab.classList.add("open");
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
// close upload tab
|
|
||||||
function closeUploadTab() {
|
|
||||||
// un-Stop scrolling
|
|
||||||
document.querySelector("html").style.overflow = "auto";
|
|
||||||
document.querySelector(".content").tabIndex = "";
|
|
||||||
|
|
||||||
// Close upload tab
|
|
||||||
const uploadTab = document.querySelector(".upload-panel");
|
|
||||||
uploadTab.classList.remove("open");
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
uploadTab.style.display = "none";
|
|
||||||
}, 250);
|
|
||||||
}
|
|
||||||
// toggle upload tab
|
|
||||||
function toggleUploadTab() {
|
|
||||||
if (document.querySelector(".upload-panel").classList.contains("open")) {
|
|
||||||
closeUploadTab();
|
|
||||||
} else {
|
|
||||||
openUploadTab();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Function to show login
|
|
||||||
function showLogin() {
|
|
||||||
popUpShow(
|
|
||||||
'idk what to put here, just login please',
|
|
||||||
'Need an account? <span class="pop-up__link" onclick="showRegister()">Register!</span>',
|
|
||||||
'<button class="pop-up__btn pop-up__btn-primary-fill" form="loginForm" type="submit">Login</button>',
|
|
||||||
'<form id="loginForm" onsubmit="return login(event)">\
|
|
||||||
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
|
|
||||||
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
|
|
||||||
</form>'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
// Function to login
|
|
||||||
function login(event) {
|
|
||||||
// AJAX takes control of subby form :3
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let formUsername = document.querySelector("#username").value;
|
|
||||||
let formPassword = document.querySelector("#password").value;
|
|
||||||
|
|
||||||
if (formUsername === "" || formPassword === "") {
|
|
||||||
addNotification("Please fill in all fields!!!!", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make form
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append("username", formUsername);
|
|
||||||
formData.append("password", formPassword);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/auth/login',
|
|
||||||
type: 'post',
|
|
||||||
data: formData,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
success: function (response) {
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
addNotification('Server exploded, F\'s in chat', 2);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
addNotification('None but devils play past here... Wrong information', 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
addNotification('Error logging in, blame someone', 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Function to show register
|
|
||||||
function showRegister() {
|
|
||||||
popUpShow(
|
|
||||||
'Who are you?',
|
|
||||||
'Already have an account? <span class="pop-up__link" onclick="showLogin()">Login!</span>',
|
|
||||||
'<button class="pop-up__btn pop-up__btn-primary-fill" form="registerForm" type="submit">Register</button>',
|
|
||||||
'<form id="registerForm" onsubmit="return register(event)">\
|
|
||||||
<input class="pop-up__input" type="text" placeholder="Namey" id="username"/>\
|
|
||||||
<input class="pop-up__input" type="text" placeholder="E mail!" id="email"/>\
|
|
||||||
<input class="pop-up__input" type="password" placeholder="Passywassy" id="password"/>\
|
|
||||||
<input class="pop-up__input" type="password" placeholder="Passywassy again!" id="password-repeat"/>\
|
|
||||||
</form>'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
// Function to register
|
|
||||||
function register(obj) {
|
|
||||||
// AJAX takes control of subby form
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let formUsername = document.querySelector("#username").value;
|
|
||||||
let formEmail = document.querySelector("#email").value;
|
|
||||||
let formPassword = document.querySelector("#password").value;
|
|
||||||
let formPasswordRepeat = document.querySelector("#password-repeat").value;
|
|
||||||
|
|
||||||
if (formUsername === "" || formEmail === "" || formPassword === "" || formPasswordRepeat === "") {
|
|
||||||
addNotification("Please fill in all fields!!!!", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make form
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append("username", formUsername);
|
|
||||||
formData.append("email", formEmail);
|
|
||||||
formData.append("password", formPassword);
|
|
||||||
formData.append("password-repeat", formPasswordRepeat);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/auth/register',
|
|
||||||
type: 'post',
|
|
||||||
data: formData,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
success: function (response) {
|
|
||||||
if (response === "gwa gwa") {
|
|
||||||
addNotification('Registered successfully! Now please login to continue', 1);
|
|
||||||
showLogin();
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < response.length; i++) {
|
|
||||||
addNotification(response[i], 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
addNotification('Server exploded, F\'s in chat', 2);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
addNotification('None but devils play past here...', 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
addNotification('Error logging in, blame someone', 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
loadOnView();
|
loadOnView();
|
||||||
|
|
128
gallery/static/js/uploadTab.js
Normal file
128
gallery/static/js/uploadTab.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Function to upload images
|
||||||
|
function uploadFile() {
|
||||||
|
// AJAX takes control of subby form
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const jobList = document.querySelector(".upload-jobs");
|
||||||
|
|
||||||
|
// Check for empty upload
|
||||||
|
if ($("#file").val() === "") {
|
||||||
|
addNotification("Please select a file to upload", 2);
|
||||||
|
} else {
|
||||||
|
// Make form
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", $("#file").prop("files")[0]);
|
||||||
|
formData.append("alt", $("#alt").val());
|
||||||
|
formData.append("description", $("#description").val());
|
||||||
|
formData.append("tags", $("#tags").val());
|
||||||
|
formData.append("submit", $("#submit").val());
|
||||||
|
|
||||||
|
// Upload the information
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/upload',
|
||||||
|
type: 'post',
|
||||||
|
data: formData,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
beforeSend: function () {
|
||||||
|
jobContainer = document.createElement("div");
|
||||||
|
jobContainer.classList.add("job");
|
||||||
|
|
||||||
|
jobStatus = document.createElement("span");
|
||||||
|
jobStatus.classList.add("job__status");
|
||||||
|
jobStatus.innerHTML = "Uploading...";
|
||||||
|
|
||||||
|
jobProgress = document.createElement("span");
|
||||||
|
jobProgress.classList.add("progress");
|
||||||
|
|
||||||
|
jobImg = document.createElement("img");
|
||||||
|
jobImg.src = URL.createObjectURL($("#file").prop("files")[0]);
|
||||||
|
|
||||||
|
jobImgFilter = document.createElement("span");
|
||||||
|
jobImgFilter.classList.add("img-filter");
|
||||||
|
|
||||||
|
jobContainer.appendChild(jobStatus);
|
||||||
|
jobContainer.appendChild(jobProgress);
|
||||||
|
jobContainer.appendChild(jobImg);
|
||||||
|
jobContainer.appendChild(jobImgFilter);
|
||||||
|
jobList.appendChild(jobContainer);
|
||||||
|
},
|
||||||
|
success: function (response) {
|
||||||
|
jobContainer.classList.add("success");
|
||||||
|
jobStatus.innerHTML = "Uploaded!";
|
||||||
|
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||||
|
addNotification("Image uploaded successfully", 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (response) {
|
||||||
|
jobContainer.classList.add("critical");
|
||||||
|
switch (response.status) {
|
||||||
|
case 500:
|
||||||
|
jobStatus.innerHTML = "Server exploded, F's in chat";
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
case 404:
|
||||||
|
jobStatus.innerHTML = "Error uploading. Blame yourself";
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
jobStatus.innerHTML = "None but devils play past here...";
|
||||||
|
break;
|
||||||
|
case 413:
|
||||||
|
jobStatus.innerHTML = "File too large!!!!!!";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
jobStatus.innerHTML = "Error uploading file, blame someone";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||||
|
addNotification("Error uploading file", 2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Empty values
|
||||||
|
$("#file").val("");
|
||||||
|
$("#alt").val("");
|
||||||
|
$("#description").val("");
|
||||||
|
$("#tags").val("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// open upload tab
|
||||||
|
function openUploadTab() {
|
||||||
|
// Stop scrolling
|
||||||
|
document.querySelector("html").style.overflow = "hidden";
|
||||||
|
document.querySelector(".content").tabIndex = "-1";
|
||||||
|
|
||||||
|
// Open upload tab
|
||||||
|
const uploadTab = document.querySelector(".upload-panel");
|
||||||
|
uploadTab.style.display = "block";
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
uploadTab.classList.add("open");
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// close upload tab
|
||||||
|
function closeUploadTab() {
|
||||||
|
// un-Stop scrolling
|
||||||
|
document.querySelector("html").style.overflow = "auto";
|
||||||
|
document.querySelector(".content").tabIndex = "";
|
||||||
|
|
||||||
|
// Close upload tab
|
||||||
|
const uploadTab = document.querySelector(".upload-panel");
|
||||||
|
uploadTab.classList.remove("open");
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
uploadTab.style.display = "none";
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle upload tab
|
||||||
|
function toggleUploadTab() {
|
||||||
|
if (document.querySelector(".upload-panel").classList.contains("open")) {
|
||||||
|
closeUploadTab();
|
||||||
|
} else {
|
||||||
|
openUploadTab();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
<div class="banner-content">
|
<div class="banner-content">
|
||||||
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.description }}</span></p>
|
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.description }}</span></p>
|
||||||
<h1><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.name }}</span></h1>
|
<h1><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.name }}</span></h1>
|
||||||
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ images|length }} Images</span></p>
|
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">By {{ group.author_username }} - {{ images|length }} Images</span></p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<div class="banner-content">
|
<div class="banner-content">
|
||||||
<p>{{ group.description }}</p>
|
<p>{{ group.description }}</p>
|
||||||
<h1>{{ group.name }}</h1>
|
<h1>{{ group.name }}</h1>
|
||||||
<p>{{ images|length }} Images</p>
|
<p>By {{ group.author_username }} - {{ images|length }} Images</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,11 +41,11 @@
|
||||||
<div class="gallery-grid">
|
<div class="gallery-grid">
|
||||||
{% for image in images %}
|
{% for image in images %}
|
||||||
<a id="image-{{ image.id }}" class="gallery-item" href="{{ url_for('group.group_post', group_id=group.id, image_id=image.id) }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
<a id="image-{{ image.id }}" class="gallery-item" href="{{ url_for('group.group_post', group_id=group.id, image_id=image.id) }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
||||||
<span class="image-filter" style="background: linear-gradient(to top, rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}), transparent)">
|
<div class="image-filter">
|
||||||
<p></p>
|
<p class="image-subtitle"></p>
|
||||||
<h2><span id="contrast-check" data-color="rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})"><span class="time">{{ image.created_at }}</span></span></h2>
|
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||||
</span>
|
</div>
|
||||||
<img data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,17 +23,23 @@
|
||||||
{% if groups %}
|
{% if groups %}
|
||||||
<div class="gallery-grid">
|
<div class="gallery-grid">
|
||||||
{% for group in groups %}
|
{% for group in groups %}
|
||||||
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}">
|
{% if group.thumbnail %}
|
||||||
<span class="image-filter">
|
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}" style="background-color: rgb({{ group.thumbnail.image_colours.0.0 }}, {{ group.thumbnail.image_colours.0.1 }}, {{ group.thumbnail.image_colours.0.2 }})">
|
||||||
<p></p>
|
<div class="image-filter">
|
||||||
<h2>{{ group.name }}</h2>
|
<p class="image-subtitle"></p>
|
||||||
</span>
|
<p class="image-title">{{ group.name }}</p>
|
||||||
{% if group.thumbnail %}
|
</div>
|
||||||
<img data-src="{{ group.thumbnail }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
<img data-src="{{ group.thumbnail.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||||
{% else %}
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}">
|
||||||
|
<div class="image-filter">
|
||||||
|
<p class="image-subtitle"></p>
|
||||||
|
<p class="image-title">{{ group.name }}</p>
|
||||||
|
</div>
|
||||||
<img src="{{ url_for('static', filename='images/error.png') }}" onload="imgFade(this)" style="opacity:0;"/>
|
<img src="{{ url_for('static', filename='images/error.png') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||||
{% endif %}
|
</a>
|
||||||
</a>
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -4,18 +4,19 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="background">
|
<div class="background">
|
||||||
<img src="/api/uploads/{{ image.file_name }}?w=1000&h=1000" onload="imgFade(this)" style="opacity:0;"/>
|
<img src="/api/uploads/{{ image.file_name }}?w=1920&h=1080" alt="{{ image.post_alt }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||||
<span style="background-image: linear-gradient(to top, rgba({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}, 1), transparent);"></span>
|
<span style="background-image: linear-gradient(to top, rgba({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}, 1), transparent);"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="image-fullscreen">
|
<div class="image-fullscreen">
|
||||||
<img src="" onload="imgFade(this);" style="opacity:0;" />
|
<img src="" alt="{{ image.post_alt }}" onload="imgFade(this);" style="opacity:0;" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="image-grid">
|
<div class="image-grid">
|
||||||
<div class="image-container" id="image-container">
|
<div class="image-container" id="image-container">
|
||||||
<img
|
<img
|
||||||
src="/api/uploads/{{ image.file_name }}?w=1000&h=1000"
|
src="/api/uploads/{{ image.file_name }}?w=1920&h=1080"
|
||||||
|
alt="{{ image.post_alt }}"
|
||||||
onload="imgFade(this)" style="opacity:0;"
|
onload="imgFade(this)" style="opacity:0;"
|
||||||
onerror="this.src='/static/images/error.png'"
|
onerror="this.src='/static/images/error.png'"
|
||||||
{% if "File" in image.image_exif %}
|
{% if "File" in image.image_exif %}
|
||||||
|
@ -119,6 +120,11 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-table">
|
<div class="info-table">
|
||||||
|
<div class="img-colours">
|
||||||
|
{% for col in image.image_colours %}
|
||||||
|
<span style="background-color: rgb({{col.0}}, {{col.1}}, {{col.2}})"></span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Image ID</td>
|
<td>Image ID</td>
|
||||||
|
@ -133,11 +139,6 @@
|
||||||
<td><span class="time">{{ image.created_at }}</span></td>
|
<td><span class="time">{{ image.created_at }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div class="img-colours">
|
|
||||||
{% for col in image.image_colours %}
|
|
||||||
<span style="background-color: rgb({{col.0}}, {{col.1}}, {{col.2}})"></span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="img-groups">
|
<div class="img-groups">
|
||||||
{% for group in image.groups %}
|
{% for group in image.groups %}
|
||||||
<a href="/group/{{ group.id }}" class="tag-icon">
|
<a href="/group/{{ group.id }}" class="tag-icon">
|
||||||
|
@ -251,7 +252,7 @@
|
||||||
'DESTRUCTION!!!!!!',
|
'DESTRUCTION!!!!!!',
|
||||||
'This will delete the image and all of its data!!! This action is irreversible!!!!! Are you sure you want to do this?????',
|
'This will delete the image and all of its data!!! This action is irreversible!!!!! Are you sure you want to do this?????',
|
||||||
'<button class="pop-up__btn pop-up__btn-critical-fill" onclick="deleteImage()">Dewww eeeet!</button>',
|
'<button class="pop-up__btn pop-up__btn-critical-fill" onclick="deleteImage()">Dewww eeeet!</button>',
|
||||||
'<img src="/api/uploads/{{ image.file_name }}?w=1000&h=1000" />'
|
'<img src="/api/uploads/{{ image.file_name }}?w=1920&h=1080" />'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
function deleteImage() {
|
function deleteImage() {
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<div class="gallery-grid">
|
<div class="gallery-grid">
|
||||||
{% for image in images %}
|
{% for image in images %}
|
||||||
<a id="image-{{ image.id }}" class="gallery-item" href="/image/{{ image.id }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
<a id="image-{{ image.id }}" class="gallery-item" href="/image/{{ image.id }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
||||||
<span class="image-filter" style="background: linear-gradient(to top, rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}), transparent)">
|
<div class="image-filter">
|
||||||
<p></p>
|
<p class="image-subtitle"></p>
|
||||||
<h2><span id="contrast-check" data-color="rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})"><span class="time">{{ image.created_at }}</span></span></h2>
|
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||||
</span>
|
</div>
|
||||||
<img data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@ from datetime import datetime
|
||||||
import sass
|
import sass
|
||||||
|
|
||||||
|
|
||||||
class CompileTheme():
|
class CompileTheme:
|
||||||
"""
|
"""
|
||||||
Compiles the theme into the static folder
|
Compiles the theme into the static folder
|
||||||
"""
|
"""
|
||||||
|
@ -33,7 +33,8 @@ class CompileTheme():
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
print(f"{now.hour}:{now.minute}:{now.second} - Done!\n")
|
print(f"{now.hour}:{now.minute}:{now.second} - Done!\n")
|
||||||
|
|
||||||
def load_sass(self, source_path, css_dest):
|
@staticmethod
|
||||||
|
def load_sass(source_path, css_dest):
|
||||||
"""
|
"""
|
||||||
Compile the sass (or scss) file into css and save it to the static folder
|
Compile the sass (or scss) file into css and save it to the static folder
|
||||||
"""
|
"""
|
||||||
|
@ -54,7 +55,8 @@ class CompileTheme():
|
||||||
|
|
||||||
print("Compiled successfully!")
|
print("Compiled successfully!")
|
||||||
|
|
||||||
def load_fonts(self, source_path, font_dest):
|
@staticmethod
|
||||||
|
def load_fonts(source_path, font_dest):
|
||||||
"""
|
"""
|
||||||
Copy the fonts folder to the static folder
|
Copy the fonts folder to the static folder
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -44,35 +44,33 @@
|
||||||
transform: scale(1.05) // scale up
|
transform: scale(1.05) // scale up
|
||||||
transition: all 0.3s cubic-bezier(.79, .14, .15, .86)
|
transition: all 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||||
|
|
||||||
> h2
|
.image-title
|
||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
|
||||||
|
|
||||||
font-size: 1rem
|
font-size: 1rem
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
|
|
||||||
color: $primary
|
color: $primary
|
||||||
|
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
|
||||||
opacity: 0 // hide
|
opacity: 0 // hide
|
||||||
transition: all 0.2s ease-in-out
|
transition: all 0.2s ease-in-out
|
||||||
|
|
||||||
> p
|
.image-subtitle
|
||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
|
||||||
|
|
||||||
font-size: 0.8rem
|
font-size: 0.8rem
|
||||||
font-weight: 500
|
font-weight: 500
|
||||||
|
|
||||||
color: $white
|
color: $white
|
||||||
|
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
|
||||||
opacity: 0 // hide
|
opacity: 0 // hide
|
||||||
transition: all 0.2s ease-in-out
|
transition: all 0.2s ease-in-out
|
||||||
|
|
||||||
img
|
img
|
||||||
width: 100%
|
width: 100%
|
||||||
|
@ -95,13 +93,14 @@
|
||||||
padding-bottom: 100%
|
padding-bottom: 100%
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
span
|
.image-filter
|
||||||
bottom: 0
|
bottom: 0
|
||||||
opacity: 1
|
opacity: 1
|
||||||
transform: scale(1)
|
transform: scale(1)
|
||||||
|
|
||||||
> h2, > p
|
.image-title,
|
||||||
opacity: 1
|
.image-subtitle
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
img
|
img
|
||||||
transform: scale(1)
|
transform: scale(1)
|
||||||
|
|
|
@ -173,14 +173,14 @@
|
||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
width: 100%
|
width: 1.5rem
|
||||||
height: 2rem
|
height: 1.5rem
|
||||||
|
|
||||||
display: flex
|
display: flex
|
||||||
justify-content: center
|
justify-content: center
|
||||||
align-items: center
|
align-items: center
|
||||||
|
|
||||||
border-radius: $rad-inner
|
border-radius: 50%
|
||||||
// border: 1px solid $white
|
// border: 1px solid $white
|
||||||
|
|
||||||
.img-groups
|
.img-groups
|
||||||
|
|
|
@ -48,7 +48,7 @@ html, body
|
||||||
color: $black
|
color: $black
|
||||||
|
|
||||||
.big-text
|
.big-text
|
||||||
height: 60vh
|
height: 20rem
|
||||||
|
|
||||||
display: flex
|
display: flex
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
|
@ -102,38 +102,8 @@ html, body
|
||||||
|
|
||||||
color: $white
|
color: $white
|
||||||
|
|
||||||
footer
|
#contrast-check
|
||||||
margin: 0
|
transition: color 0.15s ease-in-out
|
||||||
padding: 0.25rem 1rem
|
|
||||||
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
display: flex
|
|
||||||
justify-content: center
|
|
||||||
align-items: center
|
|
||||||
gap: 1rem
|
|
||||||
|
|
||||||
background-color: $white
|
|
||||||
|
|
||||||
p
|
|
||||||
margin: 0
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
font-size: 0.75rem
|
|
||||||
font-weight: 400
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
color: $black
|
|
||||||
|
|
||||||
a
|
|
||||||
margin: 0
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
font-size: 0.75rem
|
|
||||||
font-weight: 400
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
color: $primary
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: $breakpoint)
|
@media (max-width: $breakpoint)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "onlylegs"
|
name = "onlylegs"
|
||||||
version = "23.03.10"
|
version = "23.03.11"
|
||||||
description = "Gallery built for fast and simple image management"
|
description = "Gallery built for fast and simple image management"
|
||||||
authors = ["Fluffy-Bean <michal-gdula@protonmail.com>"]
|
authors = ["Fluffy-Bean <michal-gdula@protonmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
Loading…
Reference in a new issue