Browse Source

initial commit

master
cinnaboot 5 years ago
commit
60ffa66fff
  1. 9
      .gitignore
  2. 4
      MANIFEST.in
  3. 22
      gallery/__init__.py
  4. BIN
      gallery/static/default_image.png
  5. 27
      gallery/static/style.css
  6. 39
      gallery/templates/album.j2
  7. 20
      gallery/templates/index.j2
  8. 110
      gallery/views/default_routes.py
  9. 11
      setup.py
  10. 5
      wsgi.py

9
.gitignore vendored

@ -0,0 +1,9 @@
tags
*.swp
instance/
*.egg-info/
__pycache__/
venv/
build/
dist/

4
MANIFEST.in

@ -0,0 +1,4 @@
recursive-include gallery/templates *
recursive-include gallery/static *

22
gallery/__init__.py

@ -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

BIN
gallery/static/default_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

27
gallery/static/style.css

@ -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;
}

39
gallery/templates/album.j2

@ -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> &gt;</li>
<li><a href="/">{{ album }}</a> &gt;</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>

20
gallery/templates/index.j2

@ -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>

110
gallery/views/default_routes.py

@ -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")

11
setup.py

@ -0,0 +1,11 @@
from setuptools import setup
setup(
name='gallery',
version='0.1',
packages=['gallery'],
include_package_data=True,
install_requires=['flask', 'pillow']
)

5
wsgi.py

@ -0,0 +1,5 @@
#!/usr/bin/env python3
import gallery
app = gallery.create_app()
Loading…
Cancel
Save