mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
Update image gallary
This commit is contained in:
+31
-9
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user