const providerSelector = document.getElementById('provider-selector'); const searchInput = document.getElementById('main-search-input'); const resultsContainer = document.getElementById('gallery-results'); let currentPage = 1; let currentProvider = ''; let currentQuery = ''; const perPage = 48; let isLoading = false; let favorites = new Set(); let favoritesMode = false; let msnry = null; const sentinel = document.createElement('div'); sentinel.id = 'infinite-scroll-sentinel'; sentinel.style.height = '1px'; sentinel.style.marginBottom = '300px'; sentinel.style.display = 'none'; resultsContainer.parentNode.appendChild(sentinel); function getAuthHeaders(extra = {}) { const token = localStorage.getItem("token"); return token ? { ...extra, Authorization: `Bearer ${token}` } : extra; } function initializeMasonry() { if (typeof Masonry === 'undefined') { setTimeout(initializeMasonry, 100); return; } if (msnry) msnry.destroy(); msnry = new Masonry(resultsContainer, { itemSelector: '.gallery-card', columnWidth: '.gallery-card', percentPosition: true, gutter: 0, transitionDuration: '0.4s' }); msnry.layout(); } initializeMasonry(); function getTagsArray(item) { if (Array.isArray(item.tags)) return item.tags; if (typeof item.tags === 'string') { return item.tags .split(',') .map(t => t.trim()) .filter(t => t.length > 0); } return []; } function getProxiedImageUrl(item) { const imageUrl = item.image || item.image_url; if (!imageUrl || !item.headers) { return imageUrl; } let proxyUrl = `/api/proxy?url=${encodeURIComponent(imageUrl)}`; const headers = item.headers; const lowerCaseHeaders = {}; for (const key in headers) { if (Object.prototype.hasOwnProperty.call(headers, key)) { lowerCaseHeaders[key.toLowerCase()] = headers[key]; } } const referer = lowerCaseHeaders.referer; if (referer) { proxyUrl += `&referer=${encodeURIComponent(referer)}`; } const origin = lowerCaseHeaders.origin; if (origin) { proxyUrl += `&origin=${encodeURIComponent(origin)}`; } const userAgent = lowerCaseHeaders['user-agent']; if (userAgent) { proxyUrl += `&userAgent=${encodeURIComponent(userAgent)}`; } return proxyUrl; } async function loadFavorites() { try { const res = await fetch('/api/gallery/favorites', { headers: getAuthHeaders() }); if (!res.ok) return; const data = await res.json(); favorites.clear(); (data.favorites || []).forEach(fav => favorites.add(fav.id)); } catch (err) { console.error('Error loading favorites:', err); } } async function toggleFavorite(item) { const id = item.id; const isFav = favorites.has(id); try { if (isFav) { await fetch(`/api/gallery/favorites/${encodeURIComponent(id)}`, { method: 'DELETE', headers: getAuthHeaders() }); favorites.delete(id); } else { const tagsArray = getTagsArray(item); const serializedHeaders = item.headers ? JSON.stringify(item.headers) : ""; await fetch('/api/gallery/favorites', { method: 'POST', headers: getAuthHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ id: item.id, title: item.title || tagsArray.join(', ') || 'Untitled', image_url: item.image || item.image_url, thumbnail_url: item.image || item.image_url, tags: tagsArray.join(','), provider: item.provider || "", headers: serializedHeaders }) }); favorites.add(id); } document.querySelectorAll(`.gallery-card[data-id="${CSS.escape(id)}"] .fav-btn`).forEach(btn => { btn.classList.toggle('favorited', !isFav); btn.innerHTML = !isFav ? '' : ''; }); if (providerSelector.value === 'favorites' && isFav) { setTimeout(() => searchGallery(false), 300); } } catch (err) { console.error('Error toggling favorite:', err); alert('Error updating favorite'); } } function createGalleryCard(item, isLoadMore = false) { const card = document.createElement('a'); card.className = 'gallery-card grid-item'; card.href = `/gallery/${item.provider || currentProvider || 'unknown'}/${encodeURIComponent(item.id)}`; card.dataset.id = item.id; const img = document.createElement('img'); img.className = 'gallery-card-img'; img.src = getProxiedImageUrl(item); img.alt = getTagsArray(item).join(', ') || 'Image'; if (isLoadMore) { img.loading = 'lazy'; } const favBtn = document.createElement('button'); favBtn.className = 'fav-btn'; const isFav = favorites.has(item.id); favBtn.classList.toggle('favorited', isFav); favBtn.innerHTML = isFav ? '' : ''; favBtn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); toggleFavorite(item); }; card.appendChild(favBtn); card.appendChild(img); if (currentProvider !== 'favorites') { const badge = document.createElement('div'); badge.className = 'provider-badge'; badge.textContent = item.provider || 'Global'; card.appendChild(badge); } else { const badge = document.createElement('div'); badge.className = 'provider-badge'; badge.textContent = 'Favorites'; badge.style.background = 'rgba(255,107,107,0.7)'; card.appendChild(badge); } img.onload = img.onerror = () => { card.classList.add('is-loaded'); if (msnry) msnry.layout(); }; return card; } function showSkeletons(count, append = false) { const skeleton = `
${msg}
`; } if (msnry) msnry.layout(); if (!favoritesMode && data.hasNextPage) { sentinel.style.display = 'block'; observer.observe(sentinel); } else { sentinel.style.display = 'none'; observer.unobserve(sentinel); } if (isLoadMore) currentPage++; else currentPage = 1; } catch (err) { console.error('Error:', err); if (!isLoadMore) { resultsContainer.innerHTML = 'Error loading gallery
'; } } finally { isLoading = false; } } async function loadExtensions() { try { const res = await fetch('/api/extensions/gallery'); const data = await res.json(); providerSelector.innerHTML = ''; const favoritesOption = document.createElement('option'); favoritesOption.value = 'favorites'; favoritesOption.textContent = 'Favorites'; providerSelector.appendChild(favoritesOption); (data.extensions || []).forEach(ext => { const opt = document.createElement('option'); opt.value = ext; opt.textContent = ext.charAt(0).toUpperCase() + ext.slice(1); providerSelector.appendChild(opt); }); } catch (err) { console.error('Error loading extensions:', err); } } providerSelector.addEventListener('change', () => { if (providerSelector.value === 'favorites') { searchInput.placeholder = "Search in favorites..."; } else { searchInput.placeholder = "Search in gallery..."; } searchGallery(false); }); let searchTimeout; searchInput.addEventListener('input', () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchGallery(false); }, 500); }); searchInput.addEventListener('keydown', e => { if (e.key === 'Enter') { clearTimeout(searchTimeout); searchGallery(false); } }); const observer = new IntersectionObserver(entries => { if (entries[0].isIntersecting && !isLoading && !favoritesMode) { observer.unobserve(sentinel); searchGallery(true); } }, { rootMargin: '1000px' }); loadFavorites().then(() => { loadExtensions().then(() => { searchGallery(false); }); }); window.addEventListener('scroll', () => { document.getElementById('navbar')?.classList.toggle('scrolled', window.scrollY > 50); });