diff --git a/app/main.py b/app/main.py index a649dbd..48f19e8 100644 --- a/app/main.py +++ b/app/main.py @@ -26,12 +26,12 @@ def index(): return render_template('index.html', background_url=background_url) - @main.route('/gallery') @login_required def gallery(): - per_page = 35 page = request.args.get('page', 1, type=int) + per_page = request.args.get('per_page', 35, type=int) + images = ImageRecord.query.order_by( desc(func.coalesce(ImageRecord.taken_at, ImageRecord.imported_at)) @@ -41,9 +41,9 @@ def gallery(): 'gallery.html', images=images, has_next=images.has_next, - next_page_url=url_for('main.gallery', page=images.next_num) if images.has_next else '', + next_page_url=url_for('main.gallery', page=images.next_num, per_page=per_page) if images.has_next else '', has_prev=images.has_prev, - prev_page_url=url_for('main.gallery', page=images.prev_num) if images.has_prev else '' + prev_page_url=url_for('main.gallery', page=images.prev_num, per_page=per_page) if images.has_prev else '' ) @@ -54,8 +54,9 @@ def serve_image(filename): @main.route('/artist/') @login_required def artist_gallery(artist_name): - per_page = 35 page = request.args.get('page', 1, type=int) + per_page = request.args.get('per_page', 35, type=int) + images = ImageRecord.query.filter_by(artist=artist_name).order_by( desc(func.coalesce(ImageRecord.taken_at, ImageRecord.imported_at)) ).paginate(page=page, per_page=per_page) @@ -65,11 +66,12 @@ def artist_gallery(artist_name): images=images, artist=artist_name, has_next=images.has_next, - next_page_url=url_for('main.artist_gallery', artist_name=artist_name, page=images.next_num) if images.has_next else '', + next_page_url=url_for('main.artist_gallery', artist_name=artist_name, page=images.next_num, per_page=per_page) if images.has_next else '', has_prev=images.has_prev, - prev_page_url=url_for('main.artist_gallery', artist_name=artist_name, page=images.prev_num) if images.has_prev else '' + prev_page_url=url_for('main.artist_gallery', artist_name=artist_name, page=images.prev_num, per_page=per_page) if images.has_prev else '' ) + @main.route('/artists') @login_required def artist_list(): diff --git a/app/static/js/dynamic_gallery_paging.js b/app/static/js/dynamic_gallery_paging.js new file mode 100644 index 0000000..2c7bc07 --- /dev/null +++ b/app/static/js/dynamic_gallery_paging.js @@ -0,0 +1,26 @@ +document.addEventListener("DOMContentLoaded", function () { + const url = new URL(window.location.href); + const page = url.searchParams.get("page") || "1"; + const perPage = url.searchParams.get("per_page"); + + if (page !== "1" || perPage) return; + + const grid = document.querySelector(".gallery-grid"); + if (!grid) return; + + const sample = grid.querySelector(".gallery-item"); + if (!sample) return; + + const gridStyle = window.getComputedStyle(grid); + const gridGap = parseFloat(gridStyle.getPropertyValue("gap")) || 0; + const sampleWidth = sample.getBoundingClientRect().width + gridGap; + const containerWidth = grid.getBoundingClientRect().width; + + const columns = Math.floor(containerWidth / sampleWidth); + const calculatedPerPage = columns * 5; + + url.searchParams.set("per_page", calculatedPerPage); + url.searchParams.set("page", "1"); + + window.location.replace(url.toString()); +}); diff --git a/app/static/js/modal-pagination.js b/app/static/js/modal-pagination.js index 9ef1561..6bce90a 100644 --- a/app/static/js/modal-pagination.js +++ b/app/static/js/modal-pagination.js @@ -1,3 +1,4 @@ +// /app/static/js/modal-pagination.js document.addEventListener('DOMContentLoaded', () => { const modal = document.getElementById('imageModal'); const modalImage = document.getElementById('modalImage'); diff --git a/app/static/style.css b/app/static/style.css index b692168..e1f5cdb 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -1,3 +1,4 @@ +/* /app/static/style.css */ /* ========== General Layout ========== */ body { font-family: Arial, sans-serif; diff --git a/app/templates/_gallery_grid.html b/app/templates/_gallery_grid.html new file mode 100644 index 0000000..e3de371 --- /dev/null +++ b/app/templates/_gallery_grid.html @@ -0,0 +1,36 @@ + + + + + + + + +{% if request.args.get('page', '1') == '1' and not request.args.get('per_page') %} + +{% endif %} + diff --git a/app/templates/_pagination.html b/app/templates/_pagination.html index 665cb5b..1602c9b 100644 --- a/app/templates/_pagination.html +++ b/app/templates/_pagination.html @@ -1,3 +1,4 @@ +{% set per_page = request.args.get('per_page') %} {% if images.pages > 1 %}
{% set total_pages = images.pages %} @@ -5,6 +6,15 @@ {% set window_size = 7 %} {% set endpoint = request.endpoint %} + {# First and Prev #} + {% if images.has_prev %} + {% if current_page > 1 %} + « First + {% endif %} + ← Previous + {% endif %} + + {# Page window logic #} {% if total_pages <= window_size %} {% set start_page = 1 %} {% set end_page = total_pages %} @@ -19,17 +29,9 @@ {% set end_page = current_page + 3 %} {% endif %} - {# First and Prev #} - {% if images.has_prev %} - {% if current_page > 1 %} - « First - {% endif %} - ← Previous - {% endif %} - {# Left ellipsis #} {% if start_page > 1 %} - 1 + 1 {% if start_page > 2 %} {% endif %} @@ -37,7 +39,7 @@ {# Page numbers #} {% for p in range(start_page, end_page + 1) %} - {{ p }} + {{ p }} {% endfor %} {# Right ellipsis #} @@ -45,14 +47,14 @@ {% if end_page < total_pages - 1 %} {% endif %} - {{ total_pages }} + {{ total_pages }} {% endif %} {# Next and Last #} {% if images.has_next %} - Next → + Next → {% if current_page < total_pages %} - Last » + Last » {% endif %} {% endif %}
diff --git a/app/templates/artist_gallery.html b/app/templates/artist_gallery.html index d73263b..bef4084 100644 --- a/app/templates/artist_gallery.html +++ b/app/templates/artist_gallery.html @@ -1,3 +1,4 @@ + {% extends "layout.html" %} {% block title %}{{ artist }}'s Gallery{% endblock %} @@ -7,21 +8,8 @@ {% include '_pagination.html' %} - + +{% include '_gallery_grid.html' %} {% include '_pagination.html' %} diff --git a/app/templates/gallery.html b/app/templates/gallery.html index 4e0d093..26796a8 100644 --- a/app/templates/gallery.html +++ b/app/templates/gallery.html @@ -1,3 +1,4 @@ + {% extends "layout.html" %} {% block title %}Gallery{% endblock %} @@ -8,21 +9,7 @@ {% include '_pagination.html' %} - +{% include '_gallery_grid.html' %} {% include '_pagination.html' %} diff --git a/app/templates/layout.html b/app/templates/layout.html index 6a48bef..8e94661 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -56,23 +56,5 @@ {% endif %} - - - - - - diff --git a/app/utils/image_importer.py b/app/utils/image_importer.py index d9dfe8a..484a1f8 100644 --- a/app/utils/image_importer.py +++ b/app/utils/image_importer.py @@ -11,12 +11,10 @@ import uuid from app import db from app.models import ImageRecord -THUMB_DIR = "/images/thumbs" THUMB_SIZE = (400, 400) def import_images_task(source_dir, dest_dir): imported = [] - os.makedirs(THUMB_DIR, exist_ok=True) for artist_dir in os.listdir(source_dir): artist_path = os.path.join(source_dir, artist_dir) @@ -48,11 +46,9 @@ def import_images_task(source_dir, dest_dir): dest_path = os.path.join(target_dir, filename) shutil.copy2(full_path, dest_path) - # Generate thumbnail - thumb_filename = f"{uuid.uuid4().hex}.jpg" - thumb_path = os.path.join(THUMB_DIR, thumb_filename) + # Generate thumbnail AFTER copying to /images try: - generate_thumbnail(full_path, thumb_path) + thumb_path = generate_thumbnail(dest_path) except Exception as e: print(f"[WARN] Failed to generate thumbnail: {e}") thumb_path = None @@ -75,26 +71,35 @@ def import_images_task(source_dir, dest_dir): db.session.add(record) imported.append(filename) - # Add delay between each image db.session.commit() time.sleep(1) - return f"Imported {len(imported)} images." -def generate_thumbnail(image_path, size=(400, 400), overwrite=False): - thumbnail_dir = THUMB_DIR # update this accordingly - basename = os.path.basename(image_path) - thumb_path = os.path.join(thumbnail_dir, basename) - if not overwrite and os.path.exists(thumb_path): - return thumb_path +def generate_thumbnail(image_path, size=(400, 400), overwrite=False): + from pathlib import Path + + images_root = Path("/images").resolve() + image_path = Path(image_path).resolve() + + try: + rel_path = image_path.relative_to(images_root) + except ValueError: + raise ValueError(f"{image_path} is not under {images_root}") + + thumb_path = images_root / "thumbs" / rel_path + thumb_path.parent.mkdir(parents=True, exist_ok=True) + + if not overwrite and thumb_path.exists(): + return str(thumb_path) with Image.open(image_path) as img: img.thumbnail(size) img.save(thumb_path) - return thumb_path + return str(thumb_path) + def calculate_hash(file_path): hash_func = hashlib.sha256() @@ -155,4 +160,4 @@ def extract_metadata(file_path): except Exception as e: print(f"[WARN] Failed to read EXIF data: {file_path} – {e}") - return metadata \ No newline at end of file + return metadata diff --git a/image_import_worker.py b/image_import_worker.py index dd09c4a..552f826 100644 --- a/image_import_worker.py +++ b/image_import_worker.py @@ -44,6 +44,7 @@ def monitor_and_import(): print(f"[Thumbnail] Regenerated for {img.filename}") except Exception as e: print(f"[Thumbnail] Failed for {img.filename}: {e}") + time.sleep(1) except Exception as e: print(f"[Image Importer] Error during thumbnail regeneration: {e}") finally: