commit
60ffa66fff
10 changed files with 247 additions and 0 deletions
@ -0,0 +1,9 @@
|
||||
tags |
||||
*.swp |
||||
instance/ |
||||
*.egg-info/ |
||||
__pycache__/ |
||||
venv/ |
||||
build/ |
||||
dist/ |
||||
|
||||
@ -0,0 +1,4 @@
|
||||
|
||||
recursive-include gallery/templates * |
||||
recursive-include gallery/static * |
||||
|
||||
@ -0,0 +1,22 @@
|
||||
|
||||
import os |
||||
|
||||
from flask import Flask |
||||
|
||||
from .views import default_routes |
||||
|
||||
|
||||
def create_app(): |
||||
app = Flask(__name__, instance_relative_config=True) |
||||
|
||||
# NOTE: ensure intance folders exist |
||||
try: os.makedirs(app.instance_path) |
||||
except OSError: pass |
||||
try: os.makedirs(os.path.join(app.instance_path, 'images')) |
||||
except OSError: pass |
||||
try: os.makedirs(os.path.join(app.instance_path, 'thumbnails')) |
||||
except OSError: pass |
||||
|
||||
app.register_blueprint(default_routes.bp) |
||||
return app |
||||
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,27 @@
|
||||
|
||||
.nav li { display: inline; } |
||||
|
||||
.flex_container { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
width: 100%; |
||||
} |
||||
|
||||
.infocard { |
||||
margin: 0.5em; |
||||
background-color: lightgrey; |
||||
font-size: 0.7em; |
||||
} |
||||
.infocard img { border: 1px solid grey; } |
||||
.infocard ul { |
||||
list-style-type: none; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
.infocard li { |
||||
width: 160px; |
||||
white-space: nowrap; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
@ -0,0 +1,39 @@
|
||||
<!doctype html> |
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" |
||||
href="{{ url_for('static', filename='style.css') }}" |
||||
type="text/css" |
||||
/> |
||||
</head> |
||||
<body> |
||||
|
||||
<div class="nav"> |
||||
<ul> |
||||
<li><a href="/">Home</a> ></li> |
||||
<li><a href="/">{{ album }}</a> ></li> |
||||
</ul> |
||||
</div> |
||||
|
||||
<div class="flex_container"> |
||||
{% for image in images %} |
||||
<div class="infocard"> |
||||
<a href="{{ img_root + image.name }}"> |
||||
<img src="{{ url_for( |
||||
'default_routes.thumbs', |
||||
path=album, |
||||
img=image.name) |
||||
}}" /> |
||||
</a> |
||||
<ul> |
||||
<li>name: {{ image.name }}</li> |
||||
<li>date: ...</li> |
||||
<li>dimensions: ...</li> |
||||
<li>filesize: ...</li> |
||||
</ul> |
||||
</div> |
||||
{% endfor %} |
||||
</div> |
||||
|
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,20 @@
|
||||
<!doctype html> |
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" |
||||
href="{{ url_for('static', filename='style.css') }}" |
||||
type="text/css" |
||||
/> |
||||
</head> |
||||
<body> |
||||
|
||||
<div> |
||||
<ul> |
||||
{% for album in albums %} |
||||
<li><a href="/album/{{ album|urlencode }}">{{ album }}</a></li> |
||||
{% endfor %} |
||||
</ul> |
||||
</div> |
||||
|
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,110 @@
|
||||
|
||||
import os |
||||
import re |
||||
|
||||
from PIL import Image |
||||
|
||||
from flask import Blueprint, current_app, redirect, render_template, url_for |
||||
from flask import send_from_directory, Response |
||||
|
||||
|
||||
bp = Blueprint('default_routes', __name__) |
||||
VALID_CHARS = re.compile(r"^[a-zA-Z0-9_\-\.]+$") |
||||
|
||||
# |
||||
# Routes |
||||
# |
||||
|
||||
@bp.route('/') |
||||
def index() -> Response: |
||||
album_dir = os.path.join(current_app.instance_path, 'images') |
||||
album_dirs = [] |
||||
|
||||
if os.path.isdir(album_dir): |
||||
entries = os.listdir(album_dir) |
||||
for e in entries: |
||||
if os.path.isdir(os.path.join(album_dir, e)): |
||||
album_dirs.append(e) |
||||
|
||||
return render_template('index.j2', albums = sorted(album_dirs)) |
||||
|
||||
@bp.route('/album/<path>') |
||||
def album(path:str = '') -> Response: |
||||
image_dir = joinPath(current_app.instance_path, 'images') |
||||
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(getImageDetails(entry)) |
||||
|
||||
return render_template('album.j2', |
||||
img_root = f'/image/{path}/', |
||||
album = path, |
||||
images = images |
||||
) |
||||
|
||||
@bp.route('/image/<path>/<img>') |
||||
def image(path:str, img:str) -> Response: |
||||
base_dir = os.path.join(current_app.instance_path, 'images') |
||||
album_dir = os.path.join(base_dir, os.path.basename(path)) |
||||
return send_from_directory(album_dir, img) |
||||
|
||||
@bp.route('/thumbs/<path>/<img>') |
||||
def thumbs(path:str, img:str) -> Response: |
||||
base_dir = os.path.join(current_app.instance_path, 'thumbnails') |
||||
album_dir = os.path.join(base_dir, os.path.basename(path)) |
||||
return send_from_directory(album_dir, img) |
||||
|
||||
|
||||
# |
||||
# Helpers |
||||
# |
||||
|
||||
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 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([160, 160]) |
||||
im.save(joinPath(thumb_path, image.name), "JPEG") |
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
from setuptools import setup |
||||
|
||||
setup( |
||||
name='gallery', |
||||
version='0.1', |
||||
packages=['gallery'], |
||||
include_package_data=True, |
||||
install_requires=['flask', 'pillow'] |
||||
) |
||||
|
||||
Loading…
Reference in new issue