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 "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