diff --git a/links/image_api.py b/links/image_api.py index 3f4ce35..1cc7dfb 100644 --- a/links/image_api.py +++ b/links/image_api.py @@ -334,9 +334,9 @@ def random_image(request, width: Optional[int] = None, height: Optional[int] = N return HttpResponseNotFound("Invalid fit mode. Supported modes: clip, crop, fill, scale") # Validate dimensions - if width is not None and width <= 300: + if width is not None and width < 300: return HttpResponseNotFound("Width must be 300px or larger") - if height is not None and height <= 300: + if height is not None and height < 300: return HttpResponseNotFound("Height must be 300px or larger") if width is not None and width > 4096: return HttpResponseNotFound("Width too large (max 4096px)") diff --git a/links/templates/links/link_list.html b/links/templates/links/link_list.html index 7bca4a0..3267694 100644 --- a/links/templates/links/link_list.html +++ b/links/templates/links/link_list.html @@ -59,6 +59,36 @@ {% endif %} + +
+

{% trans "Random Images" %}

+
+ +
+ {% trans 'Random Image' %} +
+ + +
+ {% trans 'Random Image' %} +
+
+
+

{% trans "Link List" %}

@@ -268,6 +298,57 @@ element.textContent = localTime; }); }); + + // Random image refresh functionality + function refreshRandomImage(imgElement) { + const timestamp = Date.now(); + + // Determine if it's mobile or desktop for different sizes + const isMobile = window.innerWidth < 640; // sm breakpoint + let newUrl; + + if (imgElement.dataset.box === '1') { + // First image box + const size = isMobile ? '250' : '300'; + newUrl = `/api/images/random/${size}/${size}/?fit=crop&v=${timestamp}`; + } else { + // Second image box + const size = isMobile ? '280' : '400'; + newUrl = `/api/images/random/${size}/${size}/?fit=crop&v=${timestamp}&offset=1`; + } + + // Add loading animation + imgElement.style.opacity = '0.5'; + imgElement.style.transition = 'opacity 0.3s ease'; + + const newImg = new Image(); + newImg.onload = function() { + imgElement.src = newImg.src; + imgElement.style.opacity = '1'; + }; + newImg.onerror = function() { + // Fallback - just update timestamp on current URL + const currentUrl = new URL(imgElement.src); + currentUrl.searchParams.set('v', timestamp); + imgElement.src = currentUrl.toString(); + imgElement.style.opacity = '1'; + }; + newImg.src = newUrl; + } + + // Auto-refresh images periodically (optional) + let autoRefreshInterval; + function startAutoRefresh() { + autoRefreshInterval = setInterval(() => { + const randomImages = document.querySelectorAll('.random-image'); + randomImages.forEach((img, index) => { + setTimeout(() => refreshRandomImage(img), index * 1000); // Stagger refreshes + }); + }, 30000); // Refresh every 30 seconds + } + + // Uncomment to enable auto-refresh + // startAutoRefresh(); {% endblock %} {% endblock %} diff --git a/links/views.py b/links/views.py index cc42a8d..309aa38 100644 --- a/links/views.py +++ b/links/views.py @@ -91,6 +91,10 @@ class LinkListView(ListView): # Add latest 2 posts to the context (most recent first) context['latest_posts'] = Post.objects.all().order_by('-created_at')[:2] + + # Add timestamp for random image cache busting + import time + context['timestamp'] = int(time.time()) return context