diff --git a/viewer/image.py b/viewer/image.py index c7c3085..4b97bbf 100644 --- a/viewer/image.py +++ b/viewer/image.py @@ -8,13 +8,16 @@ import datetime # Django imports. from django.http import HttpResponseNotFound -from django.shortcuts import render +from django.shortcuts import render, redirect from django.conf import settings +from django.utils import timezone # Third-party from PIL import Image # Project imports. +from .models import Image as Im + from .common import ( normalize_sort, normalize_theme, @@ -34,6 +37,20 @@ def render_image(request, path_text, full_path): Renders the view corresponding to an image file. """ + try: + img = Im.objects.get(path=full_path, user=request.user) + + if request.method == 'POST': + img.favorite = not img.favorite + + except Im.DoesNotExist: + img = Im(path=full_path, user=request.user) + + finally: + img.last_visited = timezone.now() + img.visits = img.visits + 1 + img.save() + # Preserve query state (sort, search, theme) similar to gallery view search_text = request.GET.get("search", "").strip() sort_key = normalize_sort(request.GET.get("sort", "abc")) @@ -177,6 +194,9 @@ def render_image(request, path_text, full_path): "filesize": human_size(filesize), "created": fmt_ts(created_ts), "modified": fmt_ts(modified_ts), + "visits": img.visits, + "visited": fmt_ts(img.last_visited.timestamp()), + "favorite": img.favorite }, "breadcrumbs": breadcrumbs, "theme": theme, @@ -186,6 +206,7 @@ def render_image(request, path_text, full_path): "theme_toggle_url": gallery_url( Path(dir_path_text) if dir_path_text != "" else None, True, theme_query ), + "path": path_text, } from .common import SORT_LABELS diff --git a/viewer/migrations/0001_initial.py b/viewer/migrations/0001_initial.py new file mode 100644 index 0000000..d715196 --- /dev/null +++ b/viewer/migrations/0001_initial.py @@ -0,0 +1,52 @@ +# Generated by Django 6.0.3 on 2026-03-24 18:56 + +import django.db.models.deletion +import django.utils.timezone +import pathlib +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Image", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "path", + models.FilePathField( + path=pathlib.PurePosixPath("/home/miky/Imágenes") + ), + ), + ("favorite", models.BooleanField(default=False)), + ( + "last_visited", + models.DateTimeField(default=django.utils.timezone.now), + ), + ("visits", models.IntegerField(default=0)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/viewer/models.py b/viewer/models.py new file mode 100644 index 0000000..ec50293 --- /dev/null +++ b/viewer/models.py @@ -0,0 +1,27 @@ +from django.utils import timezone +from django.conf import settings +from django.db.models import ( + Model, + BooleanField, + DateTimeField, + IntegerField, + FilePathField, + ForeignKey, + CASCADE +) + + +class Image(Model): + """ + User relations to a specific image file by path. + """ + + user = ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False, on_delete=CASCADE) + path = FilePathField(path=settings.GALLERY_ROOT, blank=False, null=False) + favorite = BooleanField(blank=False, null=False, default=False) + last_visited = DateTimeField(blank=False, null=False, default=timezone.now) + visits = IntegerField(blank=False, null=False, default=0) + + class meta: + ordering = ["pk"] + get_latest_by = "-last_visited" diff --git a/viewer/static/css/styles.css b/viewer/static/css/styles.css index 491f86f..e8a8f4f 100644 --- a/viewer/static/css/styles.css +++ b/viewer/static/css/styles.css @@ -240,7 +240,7 @@ body { } .info-menu { - min-width: 220px; + min-width: 275px; border-radius: 8px; } diff --git a/viewer/templates/image_view.html b/viewer/templates/image_view.html index 0ed0ffb..2c6e20f 100644 --- a/viewer/templates/image_view.html +++ b/viewer/templates/image_view.html @@ -117,12 +117,23 @@ {% endfor %} +
+ {% csrf_token %} + +
+