mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2024-12-28 18:36:21 +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
|
||||
|
@ -46,7 +46,7 @@ def create_app(test_config=None):
|
|||
"""
|
||||
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()
|
||||
cache = Cache(config={'CACHE_TYPE': 'SimpleCache', 'CACHE_DEFAULT_TIMEOUT': 300})
|
||||
compress = Compress()
|
||||
|
@ -71,13 +71,11 @@ def create_app(test_config=None):
|
|||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
# Load theme
|
||||
theme_manager.CompileTheme('default', app.root_path)
|
||||
# Bundle JS files
|
||||
js = Bundle('js/*.js', output='gen/packed.js')
|
||||
assets.register('js_all', js)
|
||||
|
||||
|
||||
@app.errorhandler(403)
|
||||
@app.errorhandler(404)
|
||||
|
@ -89,7 +87,6 @@ def create_app(test_config=None):
|
|||
msg = err.description
|
||||
return render_template('error.html', error=error, msg=msg), err.code
|
||||
|
||||
|
||||
# Load login, registration and logout manager
|
||||
from . import auth
|
||||
app.register_blueprint(auth.blueprint)
|
||||
|
@ -110,10 +107,8 @@ def create_app(test_config=None):
|
|||
# Load APIs
|
||||
from . import api
|
||||
app.register_blueprint(api.blueprint)
|
||||
|
||||
|
||||
logging.info('Gallery started successfully!')
|
||||
|
||||
logging.info('Gallery started successfully!')
|
||||
|
||||
assets.init_app(app)
|
||||
cache.init_app(app)
|
||||
|
|
|
@ -19,7 +19,7 @@ from sqlalchemy.orm import sessionmaker
|
|||
|
||||
from gallery.auth import login_required
|
||||
|
||||
from . import db # Import db to create a session
|
||||
from . import db
|
||||
from . import metadata as mt
|
||||
|
||||
|
||||
|
@ -39,15 +39,15 @@ def uploads(file):
|
|||
# Get args
|
||||
width = request.args.get('w', default=0, type=int) # Width 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
|
||||
blur = request.args.get('b', default=False, type=bool) # Whether to force blur
|
||||
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
|
||||
|
||||
# if no args are passed, return the raw file
|
||||
if width == 0 and height == 0 and not filtered:
|
||||
if not os.path.exists(os.path.join(current_app.config['UPLOAD_FOLDER'],
|
||||
secure_filename(file))):
|
||||
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
|
||||
if width > 0 and height == 0:
|
||||
|
@ -60,7 +60,7 @@ def uploads(file):
|
|||
|
||||
# Open image and set extension
|
||||
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:
|
||||
logging.error('File not found: %s, possibly broken upload', file)
|
||||
abort(404)
|
||||
|
@ -79,7 +79,7 @@ def uploads(file):
|
|||
|
||||
# If has NSFW tag, blur image, etc.
|
||||
if filtered:
|
||||
#img = img.filter(ImageFilter.GaussianBlur(20))
|
||||
# img = img.filter(ImageFilter.GaussianBlur(20))
|
||||
pass
|
||||
|
||||
# If forced to blur, blur image
|
||||
|
@ -122,7 +122,7 @@ def upload():
|
|||
img_name = "GWAGWA_"+str(uuid4())
|
||||
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)
|
||||
abort(403)
|
||||
|
||||
|
@ -142,14 +142,14 @@ def upload():
|
|||
|
||||
# Save to database
|
||||
try:
|
||||
query = db.Posts(author_id = g.user.id,
|
||||
created_at = dt.utcnow(),
|
||||
file_name = img_name+'.'+img_ext,
|
||||
file_type = img_ext,
|
||||
image_exif = img_exif,
|
||||
image_colours = img_colors,
|
||||
post_description = form_description,
|
||||
post_alt = form_alt)
|
||||
query = db.Posts(author_id=g.user.id,
|
||||
created_at=dt.utcnow(),
|
||||
file_name=img_name+'.'+img_ext,
|
||||
file_type=img_ext,
|
||||
image_exif=img_exif,
|
||||
image_colours=img_colors,
|
||||
post_description=form_description,
|
||||
post_alt=form_alt)
|
||||
|
||||
db_session.add(query)
|
||||
db_session.commit()
|
||||
|
@ -159,6 +159,7 @@ def upload():
|
|||
|
||||
return 'Gwa Gwa'
|
||||
|
||||
|
||||
@blueprint.route('/delete/<int:image_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_image(image_id):
|
||||
|
@ -194,7 +195,7 @@ def delete_image(image_id):
|
|||
abort(500)
|
||||
|
||||
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'
|
||||
|
||||
|
||||
|
@ -268,7 +269,7 @@ def logfile():
|
|||
"""
|
||||
Gets the log file and returns it as a JSON object
|
||||
"""
|
||||
filename = logging.getLoggerClass().root.handlers[0].baseFilename
|
||||
filename = 'only.log'
|
||||
log_dict = {}
|
||||
i = 0
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
OnlyLegs - Authentification
|
||||
OnlyLegs - Authentication
|
||||
User registration, login and logout and locking access to pages behind a login
|
||||
"""
|
||||
import re
|
||||
|
@ -29,7 +29,7 @@ def login_required(view):
|
|||
@functools.wraps(view)
|
||||
def wrapped_view(**kwargs):
|
||||
if g.user is None or session.get('uuid') is None:
|
||||
logging.error('Authentification failed')
|
||||
logging.error('Authentication failed')
|
||||
session.clear()
|
||||
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')
|
||||
username_regex = re.compile(r'\b[A-Za-z0-9._%+-]+\b')
|
||||
|
||||
|
||||
if not username or not username_regex.match(username):
|
||||
error.append('Username is invalid!')
|
||||
|
||||
|
@ -95,7 +94,6 @@ def register():
|
|||
if error:
|
||||
return jsonify(error)
|
||||
|
||||
|
||||
try:
|
||||
register_user = db.Users(username=username,
|
||||
email=email,
|
||||
|
@ -124,7 +122,6 @@ def login():
|
|||
user = db_session.query(db.Users).filter_by(username=username).first()
|
||||
error = []
|
||||
|
||||
|
||||
if user is None:
|
||||
logging.error('User %s does not exist. Login attempt from %s',
|
||||
username, request.remote_addr)
|
||||
|
@ -137,18 +134,17 @@ def login():
|
|||
if error:
|
||||
abort(403)
|
||||
|
||||
|
||||
try:
|
||||
session.clear()
|
||||
session['user_id'] = user.id
|
||||
session['uuid'] = str(uuid.uuid4())
|
||||
|
||||
session_query = db.Sessions(user_id=user.id,
|
||||
session_uuid=session.get('uuid'),
|
||||
ip_address=request.remote_addr,
|
||||
user_agent=request.user_agent.string,
|
||||
active=True,
|
||||
created_at=dt.utcnow())
|
||||
session_uuid=session.get('uuid'),
|
||||
ip_address=request.remote_addr,
|
||||
user_agent=request.user_agent.string,
|
||||
active=True,
|
||||
created_at=dt.utcnow())
|
||||
|
||||
db_session.add(session_query)
|
||||
db_session.commit()
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""
|
||||
OnlyLegs - Database
|
||||
Database models and functions for SQLAlchemy
|
||||
OnlyLegs - Database models and functions for SQLAlchemy
|
||||
"""
|
||||
import os
|
||||
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')
|
||||
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(f'mysql://username:password@host:port/database_name', echo=False)
|
||||
# engine = create_engine('postgresql://username:password@host:port/database_name', echo=False)
|
||||
# engine = create_engine('mysql://username:password@host:port/database_name', echo=False)
|
||||
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
|
||||
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')
|
||||
|
||||
|
||||
class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
||||
class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Post table
|
||||
Joins with group_junction
|
||||
|
@ -58,7 +57,7 @@ class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
|||
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
|
||||
Joins with group_junction
|
||||
|
@ -74,7 +73,7 @@ class Groups (base): # pylint: disable=too-few-public-methods, C0103
|
|||
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
|
||||
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'))
|
||||
|
||||
|
||||
class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
||||
class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Session table
|
||||
Joins with user
|
||||
|
@ -103,7 +102,7 @@ class Sessions (base): # pylint: disable=too-few-public-methods, C0103
|
|||
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
|
||||
Joins with user
|
||||
|
@ -118,7 +117,7 @@ class Logs (base): # pylint: disable=too-few-public-methods, C0103
|
|||
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
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
Used intermally by the frontend and possibly by other applications
|
||||
Onlylegs - Image Groups
|
||||
Why groups? Because I don't like calling these albums, sounds more limiting that it actually is
|
||||
"""
|
||||
import logging
|
||||
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()
|
||||
|
||||
if thumbnail is not None:
|
||||
thumbnail_filename = db_session.query(db.Posts.file_name).filter(db.Posts.id == thumbnail[0]).first()
|
||||
group.thumbnail = thumbnail_filename[0]
|
||||
group.thumbnail = db_session.query(db.Posts.file_name,
|
||||
db.Posts.post_alt,
|
||||
db.Posts.image_colours,
|
||||
db.Posts.id).filter(db.Posts.id == thumbnail[0]).first()
|
||||
else:
|
||||
group.thumbnail = None
|
||||
|
||||
|
@ -47,6 +49,8 @@ def group(group_id):
|
|||
|
||||
if group is None:
|
||||
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()
|
||||
|
||||
|
@ -75,15 +79,12 @@ def group_post(group_id, image_id):
|
|||
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
||||
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()
|
||||
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()
|
||||
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_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:
|
||||
next = url_for('group.group_post', group_id=group_id, image_id=next[0])
|
||||
if prev is not None:
|
||||
prev = url_for('group.group_post', group_id=group_id, image_id=prev[0])
|
||||
if next_url is not None:
|
||||
next_url = url_for('group.group_post', group_id=group_id, image_id=next_url[0])
|
||||
if prev_url is not None:
|
||||
prev_url = url_for('group.group_post', group_id=group_id, image_id=prev_url[0])
|
||||
|
||||
return render_template('image.html',
|
||||
image=img,
|
||||
next_url=next,
|
||||
prev_url=prev)
|
||||
return render_template('image.html', image=img, next_url=next_url, prev_url=prev_url)
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
OnlyLegs - Metatada Parser
|
||||
OnlyLegs - Metadata Parser
|
||||
Parse metadata from images if available
|
||||
otherwise get some basic information from the file
|
||||
"""
|
||||
|
@ -11,6 +11,7 @@ from PIL.ExifTags import TAGS
|
|||
from .helpers import *
|
||||
from .mapping import *
|
||||
|
||||
|
||||
class Metadata:
|
||||
"""
|
||||
Metadata parser
|
||||
|
@ -53,7 +54,8 @@ class Metadata:
|
|||
return None
|
||||
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
|
||||
"""
|
||||
|
@ -66,15 +68,15 @@ class Metadata:
|
|||
|
||||
# Thanks chatGPT xP
|
||||
for key, value in encoded_exif.items():
|
||||
for mapping_name, mapping in EXIF_MAPPING:
|
||||
if key in mapping:
|
||||
if len(mapping[key]) == 2:
|
||||
exif[mapping_name][mapping[key][0]] = {
|
||||
for mapping_name, mapping_val in EXIF_MAPPING:
|
||||
if key in mapping_val:
|
||||
if len(mapping_val[key]) == 2:
|
||||
exif[mapping_name][mapping_val[key][0]] = {
|
||||
'raw': value,
|
||||
'formatted': getattr(helpers, mapping[key][1])(value),
|
||||
'formatted': getattr(helpers, mapping_val[key][1])(value),
|
||||
}
|
||||
else:
|
||||
exif[mapping_name][mapping[key][0]] = {
|
||||
exif[mapping_name][mapping_val[key][0]] = {
|
||||
'raw': value,
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ Metadata formatting helpers
|
|||
"""
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def human_size(value):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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):
|
||||
|
|
|
@ -61,4 +61,9 @@ FILE_MAPPING = {
|
|||
'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('/')
|
||||
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,
|
||||
db.Posts.post_alt,
|
||||
db.Posts.image_colours,
|
||||
db.Posts.created_at,
|
||||
db.Posts.id).order_by(db.Posts.id.desc()).all()
|
||||
|
||||
return render_template('index.html', images=images)
|
||||
|
||||
|
||||
@blueprint.route('/image/<int: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()
|
||||
img.groups.append(group)
|
||||
|
||||
next = 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()
|
||||
next_url = db_session.query(db.Posts.id).filter(db.Posts.id > image_id).order_by(db.Posts.id.asc()).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:
|
||||
next = url_for('gallery.image', image_id=next[0])
|
||||
if prev is not None:
|
||||
prev = url_for('gallery.image', image_id=prev[0])
|
||||
if next_url is not None:
|
||||
next_url = url_for('gallery.image', image_id=next_url[0])
|
||||
if prev_url is not None:
|
||||
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')
|
||||
def profile():
|
||||
|
@ -64,9 +67,10 @@ def profile():
|
|||
"""
|
||||
return render_template('profile.html', user_id='gwa gwa')
|
||||
|
||||
|
||||
@blueprint.route('/profile/<int:user_id>')
|
||||
def profile_id(user_id):
|
||||
"""
|
||||
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')
|
||||
|
||||
|
||||
@blueprint.route('/server')
|
||||
@login_required
|
||||
def server():
|
||||
|
@ -25,6 +26,7 @@ def server():
|
|||
"""
|
||||
return render_template('settings/server.html')
|
||||
|
||||
|
||||
@blueprint.route('/account')
|
||||
@login_required
|
||||
def account():
|
||||
|
@ -33,6 +35,7 @@ def account():
|
|||
"""
|
||||
return render_template('settings/account.html')
|
||||
|
||||
|
||||
@blueprint.route('/logs')
|
||||
@login_required
|
||||
def logs():
|
||||
|
|
|
@ -8,8 +8,10 @@ import platformdirs
|
|||
import logging
|
||||
import yaml
|
||||
|
||||
|
||||
USER_DIR = platformdirs.user_config_dir('onlylegs')
|
||||
|
||||
|
||||
class SetupApp:
|
||||
"""
|
||||
Setup the application on first run
|
||||
|
@ -36,7 +38,8 @@ class SetupApp:
|
|||
print("You can find the config files at:", USER_DIR)
|
||||
sys.exit()
|
||||
|
||||
def make_dir(self):
|
||||
@staticmethod
|
||||
def make_dir():
|
||||
"""
|
||||
Create the user directory
|
||||
"""
|
||||
|
@ -49,7 +52,8 @@ class SetupApp:
|
|||
print("Error creating user directory:", err)
|
||||
sys.exit(1)
|
||||
|
||||
def make_env(self):
|
||||
@staticmethod
|
||||
def make_env():
|
||||
"""
|
||||
Create the .env file with default values
|
||||
"""
|
||||
|
@ -67,7 +71,8 @@ class SetupApp:
|
|||
|
||||
print("Generated default .env file, please edit!")
|
||||
|
||||
def make_yaml(self):
|
||||
@staticmethod
|
||||
def make_yaml():
|
||||
"""
|
||||
Create the YAML config file with default values
|
||||
"""
|
||||
|
@ -107,17 +112,18 @@ class SetupApp:
|
|||
|
||||
print("Generated default YAML config, please edit!")
|
||||
|
||||
def logging_config(self):
|
||||
LOGS_PATH = os.path.join(platformdirs.user_config_dir('onlylegs'), 'logs')
|
||||
@staticmethod
|
||||
def logging_config():
|
||||
logs_path = os.path.join(platformdirs.user_config_dir('onlylegs'), 'logs')
|
||||
|
||||
if not os.path.isdir(LOGS_PATH):
|
||||
os.mkdir(LOGS_PATH)
|
||||
print("Created logs directory at:", LOGS_PATH)
|
||||
if not os.path.isdir(logs_path):
|
||||
os.mkdir(logs_path)
|
||||
print("Created logs directory at:", logs_path)
|
||||
|
||||
logging.getLogger('werkzeug').disabled = True
|
||||
logging.basicConfig(
|
||||
filename=os.path.join(LOGS_PATH, 'only.log'),
|
||||
filename=os.path.join(logs_path, 'only.log'),
|
||||
level=logging.INFO,
|
||||
datefmt='%Y-%m-%d %H:%M:%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 () {
|
||||
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">
|
||||
<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>
|
||||
<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>
|
||||
{% else %}
|
||||
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<div class="banner-content">
|
||||
<p>{{ group.description }}</p>
|
||||
<h1>{{ group.name }}</h1>
|
||||
<p>{{ images|length }} Images</p>
|
||||
<p>By {{ group.author_username }} - {{ images|length }} Images</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -41,11 +41,11 @@
|
|||
<div class="gallery-grid">
|
||||
{% 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 }})">
|
||||
<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)">
|
||||
<p></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>
|
||||
</span>
|
||||
<img data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||
</div>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -23,17 +23,23 @@
|
|||
{% if groups %}
|
||||
<div class="gallery-grid">
|
||||
{% for group in groups %}
|
||||
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}">
|
||||
<span class="image-filter">
|
||||
<p></p>
|
||||
<h2>{{ group.name }}</h2>
|
||||
</span>
|
||||
{% if group.thumbnail %}
|
||||
<img data-src="{{ group.thumbnail }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
{% else %}
|
||||
{% if group.thumbnail %}
|
||||
<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 }})">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title">{{ group.name }}</p>
|
||||
</div>
|
||||
<img data-src="{{ group.thumbnail.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
</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;"/>
|
||||
{% endif %}
|
||||
</a>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
|
|
@ -4,18 +4,19 @@
|
|||
|
||||
{% block content %}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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 class="image-grid">
|
||||
<div class="image-container" id="image-container">
|
||||
<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;"
|
||||
onerror="this.src='/static/images/error.png'"
|
||||
{% if "File" in image.image_exif %}
|
||||
|
@ -119,6 +120,11 @@
|
|||
</svg>
|
||||
</div>
|
||||
<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>
|
||||
<tr>
|
||||
<td>Image ID</td>
|
||||
|
@ -133,11 +139,6 @@
|
|||
<td><span class="time">{{ image.created_at }}</span></td>
|
||||
</tr>
|
||||
</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">
|
||||
{% for group in image.groups %}
|
||||
<a href="/group/{{ group.id }}" class="tag-icon">
|
||||
|
@ -251,7 +252,7 @@
|
|||
'DESTRUCTION!!!!!!',
|
||||
'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>',
|
||||
'<img src="/api/uploads/{{ image.file_name }}?w=1000&h=1000" />'
|
||||
'<img src="/api/uploads/{{ image.file_name }}?w=1920&h=1080" />'
|
||||
);
|
||||
});
|
||||
function deleteImage() {
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<div class="gallery-grid">
|
||||
{% 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 }})">
|
||||
<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)">
|
||||
<p></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>
|
||||
</span>
|
||||
<img data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||
</div>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@ from datetime import datetime
|
|||
import sass
|
||||
|
||||
|
||||
class CompileTheme():
|
||||
class CompileTheme:
|
||||
"""
|
||||
Compiles the theme into the static folder
|
||||
"""
|
||||
|
@ -33,7 +33,8 @@ class CompileTheme():
|
|||
now = datetime.now()
|
||||
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
|
||||
"""
|
||||
|
@ -54,7 +55,8 @@ class CompileTheme():
|
|||
|
||||
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
|
||||
"""
|
||||
|
|
|
@ -44,35 +44,33 @@
|
|||
transform: scale(1.05) // scale up
|
||||
transition: all 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
> h2
|
||||
margin: 0
|
||||
padding: 0
|
||||
.image-title
|
||||
margin: 0
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
|
||||
color: $primary
|
||||
color: $primary
|
||||
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
|
||||
> p
|
||||
margin: 0
|
||||
padding: 0
|
||||
.image-subtitle
|
||||
margin: 0
|
||||
|
||||
font-size: 0.8rem
|
||||
font-weight: 500
|
||||
font-size: 0.8rem
|
||||
font-weight: 500
|
||||
|
||||
color: $white
|
||||
color: $white
|
||||
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
|
||||
img
|
||||
width: 100%
|
||||
|
@ -95,13 +93,14 @@
|
|||
padding-bottom: 100%
|
||||
|
||||
&:hover
|
||||
span
|
||||
.image-filter
|
||||
bottom: 0
|
||||
opacity: 1
|
||||
transform: scale(1)
|
||||
|
||||
> h2, > p
|
||||
opacity: 1
|
||||
.image-title,
|
||||
.image-subtitle
|
||||
opacity: 1
|
||||
|
||||
img
|
||||
transform: scale(1)
|
||||
|
|
|
@ -173,14 +173,14 @@
|
|||
margin: 0
|
||||
padding: 0
|
||||
|
||||
width: 100%
|
||||
height: 2rem
|
||||
width: 1.5rem
|
||||
height: 1.5rem
|
||||
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
border-radius: $rad-inner
|
||||
border-radius: 50%
|
||||
// border: 1px solid $white
|
||||
|
||||
.img-groups
|
||||
|
|
|
@ -48,7 +48,7 @@ html, body
|
|||
color: $black
|
||||
|
||||
.big-text
|
||||
height: 60vh
|
||||
height: 20rem
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
@ -102,38 +102,8 @@ html, body
|
|||
|
||||
color: $white
|
||||
|
||||
footer
|
||||
margin: 0
|
||||
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
|
||||
#contrast-check
|
||||
transition: color 0.15s ease-in-out
|
||||
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "onlylegs"
|
||||
version = "23.03.10"
|
||||
version = "23.03.11"
|
||||
description = "Gallery built for fast and simple image management"
|
||||
authors = ["Fluffy-Bean <michal-gdula@protonmail.com>"]
|
||||
license = "MIT"
|
||||
|
|
Loading…
Reference in a new issue