Update image gallary

This commit is contained in:
2025-06-11 20:28:45 +10:00
parent 45be73e98d
commit 9ddf30610b
2 changed files with 76 additions and 27 deletions
+31 -9
View File
@@ -2,6 +2,8 @@ import os
import random
import logging
import time
import threading
from collections import deque
from pathlib import Path
from typing import Optional, Tuple, Literal, List
from PIL import Image
@@ -26,6 +28,10 @@ MAX_RANDOM_ATTEMPTS = 30 # Maximum number of attempts to find a valid random im
MINIMUM_IMAGE_SIZE = 300 # Minimum width and height for valid images
executor = ThreadPoolExecutor(max_workers=4)
# Queue to track recently served images (thread-safe)
recent_images_queue = deque(maxlen=50)
recent_images_lock = threading.Lock()
# Initialize random seed for true randomness
random.seed(time.time())
@@ -205,7 +211,7 @@ def resize_image(image: Image.Image, width: Optional[int], height: Optional[int]
return image.resize((int(orig_width * ratio), height), Image.Resampling.LANCZOS)
def get_supported_image_path() -> Optional[Path]:
"""Get a random valid image path from root folder only, without loading all files into memory."""
"""Get a random valid image path from root folder only, avoiding recently served images."""
if not IMAGES_FOLDER or not os.path.exists(IMAGES_FOLDER):
logger.warning(f"Images folder not found or not configured: {IMAGES_FOLDER}")
return None
@@ -213,6 +219,10 @@ def get_supported_image_path() -> Optional[Path]:
base_path = Path(IMAGES_FOLDER)
attempts = 0
# Get current list of recent images (thread-safe)
with recent_images_lock:
recent_images = set(recent_images_queue)
while attempts < MAX_RANDOM_ATTEMPTS:
attempts += 1
@@ -221,11 +231,21 @@ def get_supported_image_path() -> Optional[Path]:
files_in_dir = []
for item in base_path.iterdir():
if item.is_file() and item.suffix.lower() in SUPPORTED_FORMATS:
files_in_dir.append(item)
# Skip images that were recently served
if item.name not in recent_images:
files_in_dir.append(item)
if not files_in_dir:
logger.warning("No image files found in root folder")
return None
# If no non-recent images available, clear the recent queue and try again
if recent_images and attempts == 1:
logger.info("All available images were recently served, clearing recent images queue")
with recent_images_lock:
recent_images_queue.clear()
recent_images = set()
continue
else:
logger.warning("No image files found in root folder")
return None
# Ensure truly random selection
random.seed(time.time() + hash(str(os.urandom(8))))
@@ -237,8 +257,13 @@ def get_supported_image_path() -> Optional[Path]:
if selected_file.exists() and is_valid_image_fast(selected_file):
file_size_mb = selected_file.stat().st_size / (1024 * 1024)
logger.info(f"Selected random image: file='{selected_file.name}', "
f"size={file_size_mb:.2f}MB, attempt={attempts}/{MAX_RANDOM_ATTEMPTS}")
# Add to recent images queue (thread-safe)
with recent_images_lock:
recent_images_queue.append(selected_file.name)
logger.debug(f"Selected random image: file='{selected_file.name}', "
f"size={file_size_mb:.2f}MB, attempt={attempts}/{MAX_RANDOM_ATTEMPTS}, "
f"recent_queue_size={len(recent_images_queue)}")
return selected_file
@@ -329,9 +354,6 @@ def random_image(request, width: Optional[int] = None, height: Optional[int] = N
# Log the request details
client_ip = request.META.get('HTTP_X_FORWARDED_FOR',
request.META.get('REMOTE_ADDR', 'unknown'))
logger.info(f"Processing image request: width={width}, height={height}, "
f"fit={fit}, client_ip={client_ip}")
# Use thread pool for image processing
start_time = time.time()
image_data, content_type = executor.submit(
@@ -96,7 +96,7 @@
padding: 15px 25px;
display: flex;
gap: 20px;
z-index: 10;
z-index: 20;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: transform 0.3s ease, opacity 0.3s ease;
@@ -141,6 +141,32 @@
transform: rotate(45deg);
}
.next-button {
position: absolute;
bottom: 30px;
right: 120px;
width: 50px;
height: 50px;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
border-radius: 25px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 11;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
color: white;
font-size: 16px;
}
.next-button:hover {
transform: scale(1.1);
background: rgba(70, 70, 70, 0.8);
border-color: rgba(255, 255, 255, 0.3);
}
.control-group {
display: flex;
flex-direction: column;
@@ -331,16 +357,19 @@
background: rgba(50, 50, 50, 0.8);
color: #4ecdc4;
text-decoration: none;
}
@media (max-width: 768px) {
} @media (max-width: 768px) {
.control-panel {
flex-direction: column;
border-radius: 20px;
bottom: 20px;
bottom: 30px;
padding: 15px;
}
.next-button {
right: 100px;
bottom: 30px;
}
.title {
font-size: 18px;
top: 20px;
@@ -371,6 +400,9 @@
<div class="control-toggle" id="controlToggle">
<i class="fas fa-cog"></i>
</div>
<div class="next-button" id="nextBtn">
<i class="fas fa-forward"></i>
</div>
<div class="control-panel folded" id="controlPanel">
<div class="control-group">
<div class="control-label">{% trans "Animation Effect" %}</div>
@@ -384,7 +416,7 @@
<div class="control-group">
<div class="control-label">{% trans "Interval (seconds)" %}</div>
<input type="number" id="interval" min="5" max="60" value="15">
<input type="number" id="interval" min="5" max="120" value="60">
</div>
<div class="control-group">
@@ -398,20 +430,15 @@
<div class="control-group">
<div class="control-label">{% trans "Actions" %}</div>
<div style="display: flex; gap: 10px;">
<button class="btn" id="nextBtn">
<i class="fas fa-forward"></i> {% trans "Next" %}
</button>
<button class="btn" id="fullscreenBtn">
<i class="fas fa-expand"></i> {% trans "Fullscreen" %}
</button>
</div>
<button class="btn" id="fullscreenBtn">
<i class="fas fa-expand"></i> {% trans "Fullscreen" %}
</button>
</div>
</div>
<div class="timer-display">
<i class="fas fa-clock"></i>
<span id="timer">00:15</span>
<span id="timer">00:60</span>
</div>
<div class="loading" id="loading">
@@ -426,7 +453,7 @@
// Application configuration
const config = {
animationType: 'fade',
interval: 15000, // 15 seconds
interval: 60000, // 60 seconds
currentSlide: null,
nextSlide: null,
timer: null,
@@ -582,9 +609,9 @@
}
}
// Get random image ID (between 1000-1084)
// Get random image ID
function getRandomImageId() {
return Math.floor(Math.random() * 85) + 1000;
return new Date().getTime();
}
// Get image URL based on source