hopefully fixed dynamic image loading

This commit is contained in:
partisan 2024-11-13 16:59:42 +01:00
parent 3861fdc81c
commit ccba37021a
5 changed files with 250 additions and 269 deletions

View file

@ -152,6 +152,9 @@
<button name="t" value="file" class="clickable">{{ translate "torrents" }}</button>
</div>
</div>
{{ if not .JsDisabled }}
<input type="hidden" name="js_enabled" value="true">
{{ end }}
</form>
<form class="results_settings" action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}">
@ -168,48 +171,60 @@
</form>
<div class="search-results" id="results">
<!-- Results go here -->
{{ if .Results }}
<div class="images images_viewer_hidden">
<!-- Images Grid -->
{{ range $index, $result := .Results }}
<div class="image">
<img
src="/static/images/placeholder.svg"
data-id="{{ $result.ID }}"
alt="{{ .Title }}"
data-full="{{ .ProxyFull }}"
data-proxy-full="{{ .ProxyThumb }}"
class="clickable"
/>
<div class="resolution">{{ .Width }} × {{ .Height }}</div>
<div class="details">
<span class="img_title clickable">{{ .Title }}</span>
<a href="{{ .Source }}" target="_blank" class="img_source">{{ translate "source" }}</a>
</div>
<!-- Results go here -->
{{ if .Results }}
<div class="images images_viewer_hidden">
<!-- Images Grid -->
{{ range $index, $result := .Results }}
<div class="image">
{{ if $.HardCacheEnabled }}
{{ if $.JsDisabled }}
<!-- JavaScript is disabled; serve actual images -->
<img src="{{ $result.ProxyFull }}" alt="{{ $result.Title }}" class="clickable" />
{{ else }}
<!-- JavaScript is enabled; use placeholders -->
<img
src="/static/images/placeholder.svg"
data-id="{{ $result.ID }}"
data-full="{{ $result.ProxyFull }}"
data-proxy-full="{{ $result.ProxyThumb }}"
alt="{{ $result.Title }}"
class="clickable"
/>
{{ end }}
{{ else }}
<!-- HardCacheEnabled is false; serve images directly -->
<img src="{{ $result.ProxyFull }}" alt="{{ $result.Title }}" class="clickable" />
{{ end }}
<div class="resolution">{{ $result.Width }} × {{ $result.Height }}</div>
<div class="details">
<span class="img_title clickable">{{ $result.Title }}</span>
<a href="{{ $result.Source }}" target="_blank" class="img_source">{{ translate "source" }}</a>
</div>
</div>
{{ end }}
</div>
<!-- Nav buttons -->
<noscript>
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
<!-- Existing form fields -->
<input type="hidden" name="js_disabled" value="true">
</form>
<!-- Nav buttons -->
<div class="pagination">
{{ if .HasPrevPage }}
<a href="/search?q={{ .Query }}&t=image&p={{ sub .Page 1 }}&js_disabled=true">{{ translate "previous" }}</a>
{{ end }}
{{ if .HasNextPage }}
<a href="/search?q={{ .Query }}&t=image&p={{ add .Page 1 }}&js_disabled=true">{{ translate "next" }}</a>
{{ end }}
</div>
<noscript>
<div class="prev-next prev-img">
<form action="/search" method="get">
<input type="hidden" name="q" value="{{ .Query }}">
<input type="hidden" name="t" value="image">
{{ if .HasPrevPage }}
<button type="submit" name="p" value="{{ sub .Page 1 }}">{{ translate "previous" }}</button>
{{ end }}
{{ if .HasNextPage }}
<button type="submit" name="p" value="{{ add .Page 1 }}">{{ translate "next" }}</button>
{{ end }}
</form>
</div>
</noscript>
{{ else if .NoResults }}
<div class="no-results">{{ translate "no_results" .Query }}</div>
{{ else }}
<div class="no-more-results">{{ translate "no_more_results" }}</div>
{{ end }}
</noscript>
{{ else if .NoResults }}
<div class="no-results">{{ translate "no_results" .Query }}</div>
{{ else }}
<div class="no-more-results">{{ translate "no_more_results" }}</div>
{{ end }}
</div>
<div class="message-bottom-left" id="message-bottom-left">
<span>{{ translate "searching_for_new_results" }}</span>
@ -218,141 +233,133 @@
<div id="image-viewer-overlay" style="display: none;"></div>
<div id="template-data" data-page="{{ .Page }}" data-query="{{ .Query }}" data-type="image" data-hard-cache-enabled="{{ .HardCacheEnabled }}"></div>
<script defer src="/static/js/dynamicscrolling.js"></script>
<script defer src="/static/js/autocomplete.js"></script>
<script defer src="/static/js/imagetitletrim.js"></script>
<script defer src="/static/js/imageviewer.js"></script>
<!-- JavaScript to Load Images -->
<!-- JavaScript to Load Images and Dynamic loading of the page -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const templateData = document.getElementById('template-data');
const hardCacheEnabled = templateData.getAttribute('data-hard-cache-enabled') === 'true';
if (hardCacheEnabled) {
// Immediate loading of images
const images = document.querySelectorAll("img[data-id]");
images.forEach((img) => {
// Use the ProxyFull URL directly
img.src = img.dataset.proxyFull;
img.onload = function() {
img.classList.add('loaded');
};
img.onerror = function() {
console.error('Failed to load image:', img.dataset.proxyFull);
};
});
return;
}
let imageMap = {}; // Map of image IDs to img elements
let loadedImageIDs = new Set(); // Keep track of loaded image IDs
let pollingInterval = 2000; // Polling interval in milliseconds
function initializeImages() {
const images = document.querySelectorAll("img[data-id]");
images.forEach((img) => {
const id = img.dataset.id;
if (!imageMap[id]) {
imageMap[id] = img;
}
});
}
// Initialize with images present at page load
initializeImages();
// Set up MutationObserver to detect new images added to the DOM
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches && node.matches('img[data-id]')) {
const img = node;
const id = img.dataset.id;
if (!imageMap[id]) {
imageMap[id] = img;
console.log('New image added:', id);
}
} else {
// Check for nested images within added nodes
const nestedImages = node.querySelectorAll && node.querySelectorAll('img[data-id]');
if (nestedImages && nestedImages.length > 0) {
nestedImages.forEach((img) => {
const id = img.dataset.id;
if (!imageMap[id]) {
imageMap[id] = img;
console.log('New nested image added:', id);
}
});
}
}
}
});
}
}
});
// Start observing the document body for added nodes
observer.observe(document.body, { childList: true, subtree: true });
(function() {
// Configuration
const imageStatusInterval = 1000; // Interval in milliseconds to check image status
const scrollThreshold = 500; // Distance from bottom of the page to trigger loading
let isFetching = false;
let page = parseInt(document.getElementById('template-data').getAttribute('data-page')) || 1;
let query = document.getElementById('template-data').getAttribute('data-query');
let hardCacheEnabled = document.getElementById('template-data').getAttribute('data-hard-cache-enabled') === 'true';
let imageElements = [];
let imageIds = [];
// Function to check image status
function checkImageStatus() {
const imageIDs = Object.keys(imageMap).filter(id => !loadedImageIDs.has(id));
if (imageIDs.length === 0) {
console.log('All images loaded.');
if (!hardCacheEnabled) return;
if (imageIds.length === 0) {
// No images to check, do nothing
return;
}
console.log('Checking status for images:', imageIDs); // Debugging
fetch('/image_status?image_ids=' + imageIDs.join(','))
// Send AJAX request to check image status
fetch('/image_status?image_ids=' + imageIds.join(','))
.then(response => response.json())
.then(statusMap => {
console.log('Status map:', statusMap); // Debugging
let imagesStillLoading = false;
for (const [id, url] of Object.entries(statusMap)) {
const img = imageMap[id];
if (url) {
// Append cache-busting query parameter
const cacheBustingUrl = url + '?t=' + new Date().getTime();
if (img.src !== cacheBustingUrl) {
img.src = cacheBustingUrl;
img.onload = function() {
// Image loaded successfully
img.classList.add('loaded');
loadedImageIDs.add(id);
};
img.onerror = function() {
console.error('Failed to load image:', url);
};
}
} else {
imagesStillLoading = true;
imageElements = imageElements.filter(img => {
let id = img.getAttribute('data-id');
if (statusMap[id]) {
// Image is ready, update src
img.src = statusMap[id];
// Remove the image id from the list
imageIds = imageIds.filter(imageId => imageId !== id);
return false; // Remove img from imageElements
}
}
if (imagesStillLoading) {
// Poll again after a delay
setTimeout(checkImageStatus, pollingInterval);
} else {
polling = false;
console.log('All images loaded.');
}
return true; // Keep img in imageElements
});
})
.catch(error => {
console.error('Error checking image status:', error);
// Retry after a delay in case of error
setTimeout(checkImageStatus, pollingInterval * 2);
});
}
// Initialize imageElements and imageIds
if (hardCacheEnabled) {
imageElements = Array.from(document.querySelectorAll('img[data-id]'));
imageIds = imageElements.map(img => img.getAttribute('data-id'));
// Replace images with placeholders
imageElements.forEach(img => {
img.src = '/static/images/placeholder.svg';
});
// Start checking image status
let imageStatusTimer = setInterval(checkImageStatus, imageStatusInterval);
checkImageStatus(); // Initial check
}
// Infinite scrolling
window.addEventListener('scroll', function() {
if (isFetching) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - scrollThreshold) {
// User scrolled near the bottom
isFetching = true;
page += 1;
fetch('/search?q=' + encodeURIComponent(query) + '&t=image&p=' + page + '&ajax=true')
.then(response => response.text())
.then(html => {
// Parse the returned HTML and extract image elements
let parser = new DOMParser();
let doc = parser.parseFromString(html, 'text/html');
let newImages = doc.querySelectorAll('.image');
if (newImages.length > 0) {
let resultsContainer = document.querySelector('.images');
newImages.forEach(imageDiv => {
// Append new images to the container
resultsContainer.appendChild(imageDiv);
// Get the img element
let img = imageDiv.querySelector('img');
if (img) {
if (hardCacheEnabled) {
// Replace image with placeholder
img.src = '/static/images/placeholder.svg';
let id = img.getAttribute('data-id');
imageElements.push(img);
imageIds.push(id);
}
}
});
if (hardCacheEnabled) {
checkImageStatus();
}
isFetching = false;
} else {
// No more images to load
isFetching = false;
// Optionally, remove the scroll event listener if no more pages
// window.removeEventListener('scroll', scrollHandler);
}
})
.catch(error => {
console.error('Error fetching next page:', error);
isFetching = false;
});
}
});
// Remove 'js-enabled' class from content
document.getElementById('content').classList.remove('js-enabled');
})();
</script>
// Start polling every pollingInterval milliseconds
const pollingIntervalId = setInterval(checkImageStatus, pollingInterval);
// Optionally, call checkImageStatus immediately
checkImageStatus();
});
<script type="text/javascript">
// Check if 'js_enabled' is not present in the URL
if (!window.location.search.includes('js_enabled=true')) {
// Redirect to the same URL with 'js_enabled=true'
var separator = window.location.search.length ? '&' : '?';
window.location.href = window.location.href + separator + 'js_enabled=true';
}
</script>
<script>