You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.2 KiB
131 lines
3.2 KiB
|
|
import os |
|
import re |
|
|
|
from PIL import Image |
|
|
|
from flask import (Blueprint, current_app, redirect, render_template, url_for, |
|
send_from_directory, Response) |
|
|
|
|
|
bp = Blueprint('default_routes', __name__) |
|
VALID_CHARS = re.compile(r"^[a-zA-Z0-9_\-\.]+$") |
|
IMAGE_DIR = 'images' |
|
|
|
# |
|
# Routes |
|
# |
|
|
|
@bp.route('/') |
|
def index() -> Response: |
|
image_dir = getImageDir() |
|
albums = [] |
|
entries = sorted(os.listdir(image_dir)) |
|
|
|
for e in entries: |
|
albums.append(getAlbumDetails(entries, e)) |
|
|
|
return render_template('index.j2', albums = albums) |
|
|
|
@bp.route('/album/<path>') |
|
def album(path:str = '') -> Response: |
|
image_dir = getImageDir() |
|
album_dir = joinPath(image_dir, path) |
|
images = [] |
|
|
|
if not validateDir(image_dir, path): |
|
return redirect(url_for('.index')) |
|
|
|
for entry in os.scandir(album_dir): |
|
if entry.is_file(): |
|
createThumbnail(entry, path) |
|
images.append(entry.name) |
|
|
|
return render_template('album.j2', |
|
img_root = f'/image/{path}/', |
|
album = path, |
|
images = sorted(images) |
|
) |
|
|
|
@bp.route('/image/<path>/<img>') |
|
def image(path:str, img:str) -> Response: |
|
album_dir = joinPath(getImageDir(), path) |
|
return send_from_directory(album_dir, img) |
|
|
|
@bp.route('/thumbs/<path>/<img>') |
|
def thumbs(path:str, img:str) -> Response: |
|
base_dir = joinPath(current_app.instance_path, 'thumbnails') |
|
album_dir = joinPath(base_dir, path) |
|
return send_from_directory(album_dir, img) |
|
|
|
|
|
# |
|
# Helpers |
|
# |
|
|
|
def getImageDir() -> os.PathLike: |
|
return joinPath(current_app.instance_path, IMAGE_DIR) |
|
|
|
def joinPath(base:os.PathLike, path:str) -> os.PathLike: |
|
return os.path.join(base, os.path.basename(path)) |
|
|
|
def validateDir(base:os.PathLike, path:str) -> bool: |
|
if VALID_CHARS.match(path): |
|
path = joinPath(base, path) |
|
|
|
if os.path.exists(path) and os.path.isdir(path): |
|
return True |
|
|
|
return False |
|
|
|
def validateFile(base:os.PathLike, path:str) -> bool: |
|
if VALID_CHARS.match(path) and os.path.exists(joinPath(base, path)): |
|
return True |
|
|
|
return False |
|
|
|
def getAlbumDetails(dir_list:list, album:str) -> dict: |
|
image_dir = getImageDir() |
|
|
|
if not validateDir(image_dir, album): |
|
return None |
|
|
|
album_dir = joinPath(image_dir, album) |
|
entries = os.scandir(album_dir) |
|
entries = sorted(os.scandir(album_dir), |
|
key=lambda e: e.name) |
|
|
|
if len(entries) > 0: |
|
image = entries[0].name |
|
createThumbnail(entries[0], album) |
|
else: |
|
image = None |
|
|
|
return { 'name': album, 'image': image, 'count': len(entries) } |
|
|
|
|
|
|
|
def getImageDetails(entry:os.PathLike) -> dict: |
|
return { |
|
'name': entry.name, |
|
'date': '...', |
|
'dims': '...', |
|
'size': '...' |
|
} |
|
|
|
def createThumbnail(image:os.DirEntry, album:str) -> None: |
|
thumb_path = joinPath(current_app.instance_path, 'thumbnails') |
|
thumb_path = joinPath(thumb_path, album) |
|
|
|
# NOTE: check for existing thumbnail |
|
if validateFile(thumb_path, image.name): |
|
return |
|
|
|
# NOTE: ensure thumbnail album directory exists |
|
try: os.makedirs(thumb_path) |
|
except OSError: pass |
|
|
|
with Image.open(image.path) as im: |
|
im.thumbnail([300, 300]) |
|
im.save(joinPath(thumb_path, image.name), "JPEG") |
|
|
|
|