diff --git a/NibasaViewer/settings.py b/NibasaViewer/settings.py index 96108d6..2c58bb8 100644 --- a/NibasaViewer/settings.py +++ b/NibasaViewer/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.2/ref/settings/ """ +import os from pathlib import Path # 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! DEBUG = True -ALLOWED_HOSTS = ['127.0.0.1', '192.168.3.42', 'nibasa', 'imgs.nibasa'] +ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ + # Django apps. 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + + # Project apps. + 'viewer' ] MIDDLEWARE = [ @@ -116,8 +121,19 @@ USE_TZ = True # https://docs.djangoproject.com/en/4.2/howto/static-files/ STATIC_URL = 'static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field 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 diff --git a/NibasaViewer/urls.py b/NibasaViewer/urls.py index 32dfe04..028d73a 100644 --- a/NibasaViewer/urls.py +++ b/NibasaViewer/urls.py @@ -14,9 +14,26 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 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 = [ - 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) diff --git a/home/__init__.py b/home/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/home/apps.py b/home/apps.py new file mode 100644 index 0000000..e5ea0af --- /dev/null +++ b/home/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class HomeConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'home' diff --git a/home/migrations/__init__.py b/home/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/home/models.py b/home/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/home/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/home/views.py b/home/views.py new file mode 100644 index 0000000..cdeca6c --- /dev/null +++ b/home/views.py @@ -0,0 +1,5 @@ +from django.shortcuts import redirect + +# Create your views here. +def index(request): + return redirect('gallery_view_root') diff --git a/requirements.txt b/requirements.txt index 6a70635..0b7d1c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,4 @@ 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 +filetype==1.2.0 +Pillow==10.0.0 diff --git a/viewer/admin.py b/viewer/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/viewer/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/viewer/management/commands/makethumbnails.py b/viewer/management/commands/makethumbnails.py new file mode 100644 index 0000000..99edac2 --- /dev/null +++ b/viewer/management/commands/makethumbnails.py @@ -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')) diff --git a/viewer/templates/gallery_view.html b/viewer/templates/gallery_view.html new file mode 100644 index 0000000..7e5eb50 --- /dev/null +++ b/viewer/templates/gallery_view.html @@ -0,0 +1,66 @@ +{% load static %} + + + + + + Folder: {{request.path}} + + + + +
+ + + + + + +
+ {% if images|length > 0 %} +
+

+ Files +

+
+
+
+ {% for image in images %} +
+ + + +
+ {% endfor %} +
+
+ {% endif %} + {% if subdirs|length > 0 %} +
+

+ Sub-directories +

+
+
+
+ {% for dir in subdirs%} +
+ {% if request.path == '/gallery/' %} + + {% else %} + + {% endif %} +
+ +
+ + {{dir.name}} + +
+
+ {% endfor %} +
+
+ {% endif %} + + diff --git a/viewer/templates/image_view.html b/viewer/templates/image_view.html new file mode 100644 index 0000000..2913e18 --- /dev/null +++ b/viewer/templates/image_view.html @@ -0,0 +1,28 @@ +{% load static %} + + + + + + Vieweing folder: {{path}} + + + + +
+ + + + + + +
+
+
+ + + +
+
+ + diff --git a/viewer/tests.py b/viewer/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/viewer/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/viewer/urls.py b/viewer/urls.py new file mode 100644 index 0000000..8b70a8c --- /dev/null +++ b/viewer/urls.py @@ -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('/', gallery_view, name = 'gallery_view_path'), +] diff --git a/viewer/utils.py b/viewer/utils.py new file mode 100644 index 0000000..c33a266 --- /dev/null +++ b/viewer/utils.py @@ -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)) diff --git a/viewer/views.py b/viewer/views.py index 91ea44a..d5df849 100644 --- a/viewer/views.py +++ b/viewer/views.py @@ -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')