Updated documentation to reflect recent changes: - Changed image detection from MIME-type to extension-based - Added Performance Optimizations section explaining the change - Updated Code Patterns to use is_image_file() instead of filetype - Marked filetype as legacy dependency (no longer actively used) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Nibasa Viewer is a lightweight, pure HTML+CSS gallery viewer designed for compatibility with older browsers. It uses Django as a backend framework but serves images directly from the filesystem rather than using database models.
Key Design Principles:
- Filesystem-first architecture (no image/folder models in database)
- No JavaScript or modern frontend frameworks
- Broad browser compatibility (uses HTML tables, not flexbox/grid)
- Dark theme optimized for image viewing
- Lazy thumbnail generation with on-disk caching
Development Commands
# Install dependencies
pip install -r requirements.txt
# Run development server
python manage.py runserver
# Create user for gallery access (required)
python manage.py shell
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('<USERNAME>', '<EMAIL>', '<PASSWORD>')
>>> user.save()
>>> exit()
# Pre-generate all thumbnails (optional, done on-demand otherwise)
python manage.py makethumbnails
# Django admin commands
python manage.py migrate
python manage.py createsuperuser
python manage.py collectstatic
Architecture
Single App Structure
The project has one Django app: viewer. All gallery functionality is consolidated here:
views.py- Gallery directory browsing and image viewing (viewer/views.py:65-189)utils.py- Thumbnail generation helperstemplates/- Pure HTML templates (no JavaScript)static/- CSS styling and navigation icons
Gallery Data Flow
Filesystem as Data Source:
- Images served from
GALLERY_ROOTpath (configured inlocal_settings.py) - Thumbnails cached in
THUMBNAILS_ROOTpath - No database models for images or directories
- Directory structure navigated using Python's
pathlib
Static File Routing:
/imgs/→ Original images fromGALLERY_ROOT/thumbs/→ Cached thumbnails fromTHUMBNAILS_ROOT/static/→ CSS and navigation icons
Authentication
Uses Django's built-in django.contrib.auth system:
- All gallery views protected with
@login_requireddecorator - Users manually created via Django shell (no signup form)
- Database only stores user accounts (SQLite)
- Login/logout redirects configured in NibasaViewer/settings.py:108-110
View Logic
gallery_view() function (viewer/views.py:65-189) handles both:
-
Directory browsing when path points to folder:
- Lists subdirectories and images
- Supports search with recursive directory scanning (viewer/views.py:32-52)
- Paginates images at 28 per page (7 columns × 4 rows)
- Converts flat image lists to table rows for HTML rendering
-
Image viewing when path points to file:
- Displays single image with prev/next navigation
- Finds sibling images in same directory
- Links to full-resolution image
Thumbnail Generation
Lazy generation approach (viewer/utils.py):
- Thumbnails created on-demand when viewing gallery
- 128×128 pixel size (hardcoded)
- Uses Pillow for image processing
- Fast extension-based image detection (no MIME-type I/O)
Batch pre-generation:
python manage.py makethumbnailscommand available- Useful for initial setup or after adding many images
Performance Optimizations
Extension-based image filtering (viewer/utils.py:17-27):
- Uses
is_image_file()helper that checks file extensions instead of reading file contents - Supported extensions defined in
IMAGE_EXTENSIONSconstant - Eliminates ~99% of I/O operations when listing directories
- Significantly faster than MIME-type detection for large directories
Configuration
Required Local Settings
Create NibasaViewer/local_settings.py (not in git):
from pathlib import Path
GALLERY_ROOT = Path('/path/to/your/images')
THUMBNAILS_ROOT = Path('/path/to/thumbnail/cache')
# Production settings
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']
SECRET_KEY = 'your-secret-key'
Pagination Constants
Defined in viewer/views.py:23-25:
CELLS_PER_ROW = 7- Image grid columnsROWS_PER_PAGE = 4- Image grid rowsIMAGES_PER_PAGE = 28- Total images per page
Production Deployment
Systemd service files included for Gunicorn deployment:
nibasaviewer.service- Service configurationnibasaviewer.socket- Socket activation
Default production location: /var/lib/NibasaViewer
Dependencies
- Django 4.2.3 - Web framework
- Pillow 10.0.0 - Image processing (thumbnails)
- filetype 1.2.0 - Legacy dependency (no longer actively used)
- gunicorn 21.2.0 - WSGI server (production)
Code Patterns
Template rendering:
- All views use
render(request, template, context) - Context includes pagination data, image paths, and navigation state
Path handling:
- Use
pathlib.Pathfor all filesystem operations - Convert to relative paths for URLs:
image.relative_to(settings.GALLERY_ROOT) - Gallery URLs follow pattern:
/gallery/{relative_path}
Image filtering:
- Use
is_image_file(path)fromviewer.utilsto validate image files - Fast extension-based detection (checks
IMAGE_EXTENSIONSset) - Supports common formats: JPEG, PNG, GIF, WebP, BMP, TIFF, SVG
- Accepts both
pathlib.Pathand string paths