Basic functionality ready.
This commit is contained in:
@@ -10,6 +10,7 @@ For the full list of settings and their values, see
|
|||||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
@@ -25,18 +26,22 @@ SECRET_KEY = 'django-insecure-#_89g9-8to*_ogxz_e0jpnqlreo0hy10odxc_)99$cs66=#7(*
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['127.0.0.1', '192.168.3.42', 'nibasa', 'imgs.nibasa']
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
# Django apps.
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
|
# Project apps.
|
||||||
|
'viewer'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -116,8 +121,19 @@ USE_TZ = True
|
|||||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
# Gallery paths.
|
||||||
|
GALLERY_ROOT = None
|
||||||
|
THUMBNAILS_ROOT = None
|
||||||
|
|
||||||
|
# Attempt to load local settings if any.
|
||||||
|
try:
|
||||||
|
from .local_settings import *
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
@@ -14,9 +14,26 @@ Including another URLconf
|
|||||||
1. Import the include() function: from django.urls import include, path
|
1. Import the include() function: from django.urls import include, path
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import path
|
# Django imports.
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
from django.urls import (path,
|
||||||
|
include)
|
||||||
|
|
||||||
|
# Project imports
|
||||||
|
from home.views import index
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# URL Patterns. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
# Index.
|
||||||
]
|
path('', index, name = 'home'),
|
||||||
|
|
||||||
|
# Add invoices app urls.
|
||||||
|
path('gallery/', include('viewer.urls')),
|
||||||
|
] + static(settings.STATIC_URL, document_root = settings.STATIC_ROOT)\
|
||||||
|
+ static('imgs/', document_root = settings.GALLERY_ROOT)\
|
||||||
|
+ static('thumbs/', document_root = settings.THUMBNAILS_ROOT)
|
||||||
|
0
home/__init__.py
Normal file
0
home/__init__.py
Normal file
6
home/apps.py
Normal file
6
home/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class HomeConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'home'
|
0
home/migrations/__init__.py
Normal file
0
home/migrations/__init__.py
Normal file
3
home/models.py
Normal file
3
home/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
5
home/views.py
Normal file
5
home/views.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
def index(request):
|
||||||
|
return redirect('gallery_view_root')
|
@@ -1,7 +1,4 @@
|
|||||||
Django==4.2.3
|
Django==4.2.3
|
||||||
django-minify-html==1.6.0
|
|
||||||
django-pipeline==2.1.0
|
|
||||||
django-img==0.1.5
|
|
||||||
unidecode==1.3.6
|
|
||||||
Pillow==10.0.0
|
|
||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
||||||
|
filetype==1.2.0
|
||||||
|
Pillow==10.0.0
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
50
viewer/management/commands/makethumbnails.py
Normal file
50
viewer/management/commands/makethumbnails.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Standard library imports.
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# External library imports.
|
||||||
|
import filetype
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# Django imports.
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
# Project imports.
|
||||||
|
from viewer.utils import make_thumbnail
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# Constants. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
THUMB_SIZE = (128, 128)
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# Command subclass. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""
|
||||||
|
Assorted data fix and clean up commands for the Meters module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _make_missing_thumbnails(self, directory):
|
||||||
|
"""
|
||||||
|
Goes through the GALLERY_ROOT directory recursively and creates a new thumbnail
|
||||||
|
for each image that does not have a corresponding file (same name) in the THUMBNAILS_ROOT
|
||||||
|
directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make a thumbnail for each file in the current directory.
|
||||||
|
for image in [i for i in directory.iterdir() if i.is_file() and filetype.is_image(str(i))]:
|
||||||
|
make_thumbnail(image)
|
||||||
|
|
||||||
|
# Handle each sub-directory recursively.
|
||||||
|
for subdir in [i for i in directory.iterdir() if i.is_dir()]:
|
||||||
|
self._make_missing_thumbnails(subdir)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
try:
|
||||||
|
self._make_missing_thumbnails(settings.GALLERY_ROOT)
|
||||||
|
|
||||||
|
except KeyboardInterrupt as e:
|
||||||
|
self.stderr.write(self.style.ERROR('\nInterrupted'))
|
66
viewer/templates/gallery_view.html
Normal file
66
viewer/templates/gallery_view.html
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height" />
|
||||||
|
<title>
|
||||||
|
Folder: {{request.path}}
|
||||||
|
</title>
|
||||||
|
<link href="{% static 'css/styles.css' %}" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="background">
|
||||||
|
<div class="centered-container">
|
||||||
|
<a href="/gallery/">
|
||||||
|
<img src="{% static 'imgs/gohome.png' %}">
|
||||||
|
</a>
|
||||||
|
<a href="..">
|
||||||
|
<img src="{% static 'imgs/back.png' %}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% if images|length > 0 %}
|
||||||
|
<div class="centered-container">
|
||||||
|
<h1>
|
||||||
|
Files
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class=grid-container>
|
||||||
|
<div class="grid">
|
||||||
|
{% for image in images %}
|
||||||
|
<div class="column">
|
||||||
|
<a href="{{image.path}}">
|
||||||
|
<img src="{{image.thumbnail}}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if subdirs|length > 0 %}
|
||||||
|
<div class="centered-container">
|
||||||
|
<h1>
|
||||||
|
Sub-directories
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class=grid-container>
|
||||||
|
<div class="grid">
|
||||||
|
{% for dir in subdirs%}
|
||||||
|
<div class="column">
|
||||||
|
{% if request.path == '/gallery/' %}
|
||||||
|
<a href="{{dir.name}}">
|
||||||
|
{% else %}
|
||||||
|
<a href="{{request.path}}{{dir.name}}">
|
||||||
|
{% endif %}
|
||||||
|
<div class="grid-icon">
|
||||||
|
<img src="{% static 'imgs/folder-pictures.png' %}">
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
|
{{dir.name}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
28
viewer/templates/image_view.html
Normal file
28
viewer/templates/image_view.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html style="height:100%;">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height" />
|
||||||
|
<title>
|
||||||
|
Vieweing folder: {{path}}
|
||||||
|
</title>
|
||||||
|
<link href="{% static 'css/styles.css' %}" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="background">
|
||||||
|
<div class="centered-container">
|
||||||
|
<a href="/gallery/">
|
||||||
|
<img src="{% static 'imgs/gohome.png' %}">
|
||||||
|
</a>
|
||||||
|
<a href="..">
|
||||||
|
<img src="{% static 'imgs/back.png' %}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="centered-container">
|
||||||
|
<div class="image-container">
|
||||||
|
<a href="{{image_path}}" target="_blank">
|
||||||
|
<img src="{{image_path}}" class="image">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
18
viewer/urls.py
Normal file
18
viewer/urls.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Django imports
|
||||||
|
from django.urls import path
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
# Module imports
|
||||||
|
from .views import (
|
||||||
|
gallery_view
|
||||||
|
)
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# URL Patterns. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Views.
|
||||||
|
path('', gallery_view, name = 'gallery_view_root'),
|
||||||
|
path('<path:path>/', gallery_view, name = 'gallery_view_path'),
|
||||||
|
]
|
29
viewer/utils.py
Normal file
29
viewer/utils.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Standard library imports.
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# External library imports.
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# Django imports.
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# Helper functions. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
def make_thumbnail(image):
|
||||||
|
"""
|
||||||
|
Creates a thumbnail in the corresponding THUMBNAILS_ROOT directory for the given image.
|
||||||
|
|
||||||
|
:param image: A pathlib.Path instance pointing to a file inside the GALLERY_ROOT directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if image.exists():
|
||||||
|
thumb_path = Path(str(image).replace(str(settings.GALLERY_ROOT), str(settings.THUMBNAILS_ROOT)))
|
||||||
|
|
||||||
|
thumb_path.parent.mkdir(parents = True, exist_ok = True)
|
||||||
|
|
||||||
|
if not thumb_path.exists():
|
||||||
|
with Image.open(str(image)) as pilimg:
|
||||||
|
pilimg.thumbnail(THUMB_SIZE)
|
||||||
|
pilimg.save(str(thumb_path))
|
@@ -1,3 +1,57 @@
|
|||||||
from django.shortcuts import render
|
# Standard library imports.
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# Create your views here.
|
# External library imports.
|
||||||
|
import filetype
|
||||||
|
|
||||||
|
# Django imports.
|
||||||
|
from django.http import HttpResponseNotFound
|
||||||
|
from django.conf import settings
|
||||||
|
from django.shortcuts import (render,
|
||||||
|
redirect)
|
||||||
|
|
||||||
|
# Project imports.
|
||||||
|
from .utils import make_thumbnail
|
||||||
|
|
||||||
|
###########################################################################################
|
||||||
|
# View functions. #
|
||||||
|
###########################################################################################
|
||||||
|
|
||||||
|
def gallery_view(request, path = None):
|
||||||
|
"""
|
||||||
|
Shows a list of subdirectories and image files inside the given path.
|
||||||
|
The path should be inside the GALLERY_ROOT path, otherwise a 404 error will be thrown.
|
||||||
|
"""
|
||||||
|
|
||||||
|
full_path = settings.GALLERY_ROOT.joinpath(path) if path is not None else settings.GALLERY_ROOT
|
||||||
|
|
||||||
|
if full_path.exists():
|
||||||
|
if full_path.is_dir():
|
||||||
|
subdirs = sorted([i for i in full_path.iterdir() if i.is_dir()])
|
||||||
|
images = sorted([i for i in full_path.iterdir() if i.is_file() and filetype.is_image(str(i))])
|
||||||
|
display_images = []
|
||||||
|
|
||||||
|
for image in images:
|
||||||
|
make_thumbnail(image)
|
||||||
|
|
||||||
|
img_context = {
|
||||||
|
'path': image.name,
|
||||||
|
'name': image.name,
|
||||||
|
'thumbnail': '/thumbs/' + path + '/' + image.name if path is not None else '/thumbs/' + image.name
|
||||||
|
}
|
||||||
|
|
||||||
|
display_images.append(img_context)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'path': path,
|
||||||
|
'subdirs': subdirs,
|
||||||
|
'images': display_images
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'gallery_view.html', context)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return render(request, 'image_view.html', {'image_path': Path('/imgs/').joinpath(path)})
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseNotFound('Not found')
|
||||||
|
Reference in New Issue
Block a user