PageSourceSearch

https://theblackholebay.lol/js/app.js

js theblackholebay.lol collected 2026-10-09 01:12:31 UTC 19,888 bytes, 133 lines download raw bytes

1const state = { query: '', results: [], visibleResults: [], selectedCategory: 'all', selectedQuickFilters: new Set(), hasSearched: false, currentPage: 1, sortBy: 'relevance', sortOrder: 'desc', itemsPerPage: 25, userFilter: '' };
2const catMap = { all: '0', video: '200', audio: '100', apps: '300', games: '400', books: '601', porn: '500', other: '699' };
3const categoryDefinitions = [ { key: 'all', label: 'All' }, { key: 'video', label: 'Video' }, { key: 'audio', label: 'Audio' }, { key: 'apps', label: 'Apps' }, { key: 'games', label: 'Games' }, { key: 'books', label: 'Books' }, { key: 'porn', label: 'Porn' }, { key: 'other', label: 'Other' } ];
4const quickFilters = ['720p', '1080p', '2160p', 'x264', 'h264', 'x265', 'h265', 'WEBRip', 'BluRay', 'HDR', 'HEVC'];
5let carouselIndex = 0, carouselItems = [], carouselTab = 'movie', carouselTimer = null;
6let searchRequestId = 0;
7
8fetch('/api/settings').then(r => r.json()).then(s => { if (s.developmentMode) { document.body.classList.add('dev-mode-active'); var b = document.getElementById('dev-banner'); if (b) b.style.display = 'block'; } if (s.customBanner) { var cb = document.getElementById('custom-banner'); if (cb) { cb.textContent = s.customBanner; cb.style.display = 'block'; document.body.style.paddingTop = '40px'; } } }).catch(() => {});
9function setMessage(m, e) { var b = document.getElementById('status-message'); if (!b) return; b.textContent = m; b.style.color = e ? '#e74c3c' : '#888888'; }
10function toggleLoader(s) { var l = document.getElementById('loader'), list = document.getElementById('results-list'), pg = document.getElementById('pagination-controls'); if (!l) return; if (s) { l.classList.remove('hidden'); if (list) list.innerHTML = ''; if (pg) pg.classList.add('hidden'); setMessage(''); } else l.classList.add('hidden'); }
11function escapeHtml(v) { return String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
12function formatDate(v) { var t = Number(v); if (!t) return 'Unknown'; var d = new Date(t*1000); return isNaN(d.getTime()) ? 'Unknown' : d.toLocaleString(); }
13function getCategoryLabel(c) { var v = Number(c||0); if (v>=100&&v<200) return 'Audio'; if (v>=200&&v<300) return 'Video'; if (v>=300&&v<400) return 'Apps'; if (v>=400&&v<500) return 'Games'; if (v>=500&&v<600) return 'Porn'; if (v>=600&&v<700) return 'Other'; return 'Other'; }
14function matchesCategory(item) { var l = getCategoryLabel(item.category).toLowerCase(); var n = item.name.toLowerCase(); if (state.selectedCategory==='all') return l!=='porn'; if (state.selectedCategory==='video') return l==='video'; if (state.selectedCategory==='audio') return l==='audio'; if (state.selectedCategory==='apps') return l==='apps'; if (state.selectedCategory==='games') return l==='games'; if (state.selectedCategory==='books') return l==='other'&&/epub|pdf|ebook|comic|book/i.test(n); if (state.selectedCategory==='porn') return l==='porn'; if (state.selectedCategory==='other') return l==='other'&&!/epub|pdf|ebook|comic|book/i.test(n); return true; }
15function matchesQuickFilters(item) { if (!state.selectedQuickFilters.size) return true; var ln = item.name.toLowerCase(); return Array.from(state.selectedQuickFilters).some(f => ln.includes(f.toLowerCase())); }
16function matchesUserFilter(item) { if (!state.userFilter) return true; return item.name.toLowerCase().includes(state.userFilter.toLowerCase()); }
17function renderCategoryFilters() { var c = document.getElementById('top-category-filters')||document.getElementById('category-filters'); if (!c) return; c.innerHTML = categoryDefinitions.map(e => '<button class="chip '+(state.selectedCategory===e.key?'active':'')+'" data-category="'+e.key+'" type="button">'+e.label+'</button>').join(''); }
18function renderQuickFilters() { var c = document.getElementById('quick-filters'); if (!c) return; c.innerHTML = quickFilters.map(f => '<button class="chip '+(state.selectedQuickFilters.has(f)?'active':'')+'" data-quick-filter="'+f+'" type="button">'+f+'</button>').join('') + '<div class="filter-input-wrap"><input type="text" id="user-filter-input" value="'+escapeHtml(state.userFilter)+'" placeholder="Filter results..."></div>'; }
19
20function loadRecentSearches() { var s = JSON.parse(localStorage.getItem('bhb_searches')||'[]'); var c = document.getElementById('recent-searches-container'); if (!c) return; if (s.length>0) { c.style.display='flex'; c.innerHTML = s.map(q => '<div class="chip recent-search-chip" data-query="'+escapeHtml(q)+'"><span>'+escapeHtml(q)+'</span><button class="remove-x" data-remove="'+escapeHtml(q)+'" title="Remove" style="background:transparent;border:none;color:#888;cursor:pointer;margin-left:5px;">x</button></div>').join(''); } else { c.style.display='none'; c.innerHTML=''; } }
21function addRecentSearch(q) { if (!q) return; var s = JSON.parse(localStorage.getItem('bhb_searches')||'[]'); s = s.filter(x => x.toLowerCase()!==q.toLowerCase()); s.unshift(q); s = s.slice(0,10); localStorage.setItem('bhb_searches', JSON.stringify(s)); loadRecentSearches(); }
22function removeRecentSearch(q) { var s = JSON.parse(localStorage.getItem('bhb_searches')||'[]'); s = s.filter(x => x!==q); localStorage.setItem('bhb_searches', JSON.stringify(s)); loadRecentSearches(); }
23
24function openTorrentDetails(btn) { var id = btn.getAttribute('data-id'); if (!id) return; window.location.assign('/torrent.html?id='+encodeURIComponent(id)); }
25function trackDownload() { fetch('/api/track/download').catch(console.error); }
26function sortResults(r) { var sb=state.sortBy, so=state.sortOrder; if (!sb) return r; if (sb==='relevance') return [...r]; return [...r].sort((a,b)=>{var vA,vB; if(sb==='category'){vA=getCategoryLabel(a.category).toLowerCase();vB=getCategoryLabel(b.category).toLowerCase();}else if(sb==='name'){vA=a.name.toLowerCase();vB=b.name.toLowerCase();}else if(sb==='added'){vA=Number(a.added||0);vB=Number(b.added||0);}else if(sb==='size'){vA=Number(a.size||0);vB=Number(b.size||0);}else if(sb==='seeders'){vA=Number(a.seeders||0);vB=Number(b.seeders||0);}else if(sb==='leechers'){vA=Number(a.leechers||0);vB=Number(b.leechers||0);}else if(sb==='username'){vA=(a.username||'Anonymous').toLowerCase();vB=(b.username||'Anonymous').toLowerCase();} if(vA<vB)return so==='asc'?-1:1; if(vA>vB)return so==='asc'?1:-1; return 0;}); }
27function updateSortHeaders() { document.querySelectorAll('.results-table th[data-sort]').forEach(th=>{var k=th.getAttribute('data-sort');th.classList.remove('sorted-asc','sorted-desc');if(k===state.sortBy)th.classList.add(state.sortOrder==='asc'?'sorted-asc':'sorted-desc');}); }
28function renderPagination(t) { var c=document.getElementById('pagination-controls'),pi=document.getElementById('page-info');if(!c||!pi)return;
28var tp=Math.ceil(t/state.itemsPerPage)||1;if(tp<=1&&state.itemsPerPage>=9999){c.classList.add('hidden');return;}c.classList.remove('hidden');pi.textContent='Page '+state.currentPage+' of '+tp;var pb=document.getElementById('prev-page-btn'),nb=document.getElementById('next-page-btn');if(pb)pb.disabled=state.currentPage===1;if(nb)nb.disabled=state.currentPage===tp; }
29function renderResults() { var list=document.getElementById('results-list'),h=document.getElementById('results-heading'),st=document.getElementById('results-subtitle'),p=document.getElementById('results-panel');if(!list)return;if(!state.hasSearched){if(p)p.classList.add('hidden');return;}if(p)p.classList.remove('hidden');var f=state.results.filter(i=>matchesCategory(i)&&matchesQuickFilters(i)&&matchesUserFilter(i));state.visibleResults=f;var s=sortResults(f);var t=s.length;var tp=Math.ceil(t/state.itemsPerPage)||1;if(state.currentPage>tp)state.currentPage=tp;var si=(state.currentPage-1)*state.itemsPerPage,pg=s.slice(si,si+state.itemsPerPage);if(h)h.textContent=state.query?'Results for: '+state.query:'Browse Latest Torrents';if(st)st.textContent=t?'Showing '+(si+1)+'-'+Math.min(si+state.itemsPerPage,t)+' of '+t+' results':'No matching results';updateSortHeaders();renderPagination(t);
29if(!state.results.length){list.innerHTML='<tr><td colspan="7" style="text-align:center;padding:20px;color:#888;">No results found for "'+escapeHtml(state.query)+'".</td></tr>';return;}if(!f.length){list.innerHTML='<tr><td colspan="7" style="text-align:center;padding:20px;color:#888;">No results match the selected filters.</td></tr>';return;}list.innerHTML=pg.map(item=>{var bh='';if(item.status==='vip')bh='&nbsp;<img src="/images/blackhole-green.gif" style="width:24px;height:24px;vertical-align:middle;" title="VIP" alt="VIP"/>';else if(item.status==='trusted')bh='&nbsp;<img src="/images/blackhole-pink.gif" style="width:24px;height:24px;vertical-align:middle;" title="Trusted" alt="Trusted"/>';else if(item.isLocal)bh='&nbsp;<img src="/images/orange_black_hole.png" style="width:24px;height:24px;vertical-align:middle;" title="Uploader" alt="Uploader"/>';var ut='<span style="color:#888;">'+escapeHtml(item.username||'Anonymous')+'</span>';var mi=item.magnet?'<a href="'+escapeHtml(item.magnet)+'" target="_blank" title="Download" style="margin-left:10px;display:inline-block;" onclick="trackDownload()"><img src="/images/icon-magnet.gif" alt="Magnet" style="width:14px;height:14px;vertical-align:middle;"/></a>':'';return '<tr style="border-bottom:1px solid #1a1a1a;"><td style="padding:15px;">
29<span style="background:#020202;border:1px solid #1a1a1a;padding:4px 8px;border-radius:4px;font-size:12px;color:#888;">'+escapeHtml(getCategoryLabel(item.category))+'</span></td><td style="padding:15px;"><button class="title-link" type="button" data-id="'+escapeHtml(item.id)+'" onclick="openTorrentDetails(this)">'+escapeHtml(item.name)+'</button>'+mi+'</td><td style="padding:15px;color:#888;font-size:14px;">'+escapeHtml(formatDate(item.added))+'</td><td style="padding:15px;color:#888;font-size:14px;">'+escapeHtml(item.sizeLabel||item.size||'0 B')+'</td><td style="padding:15px;color:#e0e0e0;font-weight:bold;">'+item.seeders+'</td><td style="padding:15px;color:#e0e0e0;font-weight:bold;">'+item.leechers+'</td><td style="padding:15px;font-size:14px;">'+ut+bh+'</td></tr>';}).join(''); }
30
31async function fetchTorrents(query, catCode) {
32  catCode = catCode || '0';
33  searchRequestId++;
34  var myId = searchRequestId;
35  toggleLoader(true); state.userFilter=''; state.results=[];
36  state.selectedCategory=Object.keys(catMap).find(k=>catMap[k]===catCode)||'all';
37  state.currentPage=1; 
38  state.sortBy='relevance'; state.sortOrder='desc';
39  try {
40    var response=await fetch('/api/search?q='+encodeURIComponent(query)+'&category='+catCode+'&page=0');
41    if (myId !== searchRequestId) return;
42    
43    if(response.status === 403) {
44      var d = await response.json();
45      if (myId !== searchRequestId) return;
46      if (d.error && d.error.includes('WARNING')) {
47        setMessage(d.error, true); 
48      } else {
49        document.body.innerHTML = '<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;background:#111;color:#e74c3c;font-family:sans-serif;text-align:center;padding:20px;"><h1 style="font-size:3rem;margin:0;">ACCESS DENIED</h1><h2 style="font-size:1.5rem;margin:10px 0 20px 0;">IP & Device Permanently Banned</h2><p style="max-width:500px;color:#ccc;">'+d.error+'</p></div>';
50      }
51      toggleLoader(false);
52      return;
53    }
54
55    var payload=await response.json();
56    if (myId !== searchRequestId) return;
57    toggleLoader(false);
58    if(!response.ok||payload.error){setMessage(payload.error||'Search failed',true);return;}
59    state.query=query; state.hasSearched=true; state.results=payload.results||[];
60    if(query)addRecentSearch(query);
61    renderCategoryFilters(); renderQuickFilters(); renderResults();
62    var fb=document.getElementById('fallback-banner'),cb=document.getElementById('custom-banner');
63    if(payload.source==='local_fallback'||payload.source==='local_fallback+1337x'){if(fb){fb.style.display='block';document.body.style.paddingTop='40px';}if(!state.results.length)setMessage('Remote API unavailable, no torrents match "'+state.query+'".');else setMessage('Showing '+state.results.length+' results from Local DB & 1337x.');}else{if(fb)fb.style.display='none';if(!cb||cb.style.display!=='block')document.body.style.paddingTop='0';if(!state.results.length)setMessage('No results found for "'+state.query+'".');else setMessage('Found '+state.results.length+' results.');}
64  } catch(err) {
65    if (myId !== searchRequestId) return;
66    toggleLoader(false); setMessage('An error occurred while searching.', true);
67  }
68}
69
70function normalizeSlide(item, type) {
71  if (type === 'movie') { var img = item.backdrop_path ? 'https://image.tmdb.org/t/p/original'+item.backdrop_path : (item.poster_path ? 'https://image.tmdb.org/t/p/original'+item.poster_path : null); return { title: item.title || item.name, image: img, subtitle: '', search: item.title || item.name }; }
72  if (type === 'tv') { var img2 = item.backdrop_path ? 'https://image.tmdb.org/t/p/original'+item.backdrop_path : (item.poster_path ? 'https://image.tmdb.org/t/p/original'+item.poster_path : null); return { title: item.name || item.title, image: img2, subtitle: '', search: item.name || item.title }; }
73  if (type === 'game') { var sub=''; if(item.rating) sub='\u2605 '+item.rating; if(item.savings) sub+=(sub?' \u00b7 ':'')+item.savings; if(!sub&&item.price) sub=item.price; return { title: item.name, image: item.background_image, subtitle: sub, search: item.name }; }
74  return null;
75}
76
77async function loadCarouselData(type) {
78  var ep; if(type==='movie')ep='/api/trending'; else if(type==='tv')ep='/api/trending_tv'; else if(type==='game')ep='/api/trending_games'; else return;
79  try { var res=await fetch(ep); if(!res.ok)return; var data=await res.json(); carouselItems=(data.results||[]).slice(0,10).map(i=>normalizeSlide(i,type)).filter(s=>s&&s.title); renderCarouselSlides(); } catch(e) { console.error('Carousel error:', e); }
80}
81
82function renderCarouselSlides() {
83  var c=document.getElementById('movie-carousel'); if(!c)return; if(!carouselItems.length){c.innerHTML='';return;}
84  c.innerHTML=carouselItems.map(s=>{var ih=s.image?'<img src="'+escapeHtml(s.image)+'" alt="'+escapeHtml(s.title)+'" loading="lazy">':'<div style="width:100%;height:200px;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);"></div>';return '<div class="carousel-slide" data-search="'+escapeHtml(s.search)+'">'+ih+'<div class="carousel-overlay"><h3>'+escapeHtml(s.title)+'</h3>'+(s.subtitle?'<p class="sub">'+escapeHtml(s.subtitle)+'</p>':'')+'</div></div>';}).join('');
85  document.querySelectorAll('.carousel-slide').forEach(sl=>{sl.addEventListener('click',function(){var t=sl.getAttribute('data-search');if(t)searchFromCarousel(t);});});
86  var ct=document.getElementById('carousel-container'); if(ct)ct.style.display='block'; carouselIndex=0; updateCarousel();
87  if(carouselTimer)clearInterval(carouselTimer); carouselTimer=setInterval(function(){if(carouselItems.length>0){carouselIndex=(carouselIndex<carouselItems.length-1)?carouselIndex+1:0;updateCarousel();}},7000);
88}
89function updateCarousel() { var c=document.getElementById('movie-carousel'); if(c)c.style.transform='translateX(-'+(carouselIndex*100)+'%)'; }
90window.searchFromCarousel = function(title) { searchRequestId++; var i=document.getElementById('search-input'); if(i)i.value=title; fetchTorrents(title, catMap[state.selectedCategory]||'0'); var p=document.getElementById('results-panel'); if(p)p.scrollIntoView({behavior:'smooth'}); };
91function switchCarouselTab(tab) { carouselTab=tab; document.querySelectorAll('.carousel-tab').forEach(t=>t.classList.toggle('active',t.getAttribute('data-tab')===tab)); loadCarouselData(tab); }
92
93function attachHandlers() {
94  var form=document.getElementById('search-form'),input=document.getElementById('search-input'),catBar=document.getElementById('top-category-filters')||document.getElementById('category-filters'),qb=document.getElementById('quick-filters'),ipp=document.getElementById('items-per-page-select'),rsc=document.getElementById('recent-searches-container');
95  if(form&&input)form.addEventListener('submit',function(e){e.preventDefault();var q=input.value.trim();if(!q){fetchTorrents('',catMap[state.selectedCategory]||'0');return;}fetchTorrents(q,catMap[state.selectedCategory]||'0');});
96  if(ipp)ipp.addEventListener('change',function(e){state.itemsPerPage=Number(e.target.value);state.currentPage=1;renderResults();});
97  if(catBar)catBar.addEventListener('click',function(e){var b=e.target.closest('[data-category]');if(!b)return;state.selectedCategory=b.getAttribute('data-category');state.currentPage=1;renderCategoryFilters();fetchTorrents(state.query,catMap[state.selectedCategory]||'0');});
98  if(qb){qb.addEventListener('click',function(e){var b=e.target.closest('[data-quick-filter]');if(!b)return;var f=b.getAttribute('data-quick-filter');if(state.selectedQuickFilters.has(f))state.selectedQuickFilters.delete(f);else state.selectedQuickFilters.add(f);renderQuickFilters();renderResults();});qb.addEventListener('input',function(e){if(e.target.id==='user-filter-input'){state.userFilter=e.target.value;state.currentPage=1;renderResults();}});}
99  if(rsc)rsc.addEventListener('click',function(e){var rb=e.target.closest('.remove-x');if(rb){e.preventDefault();e.stopPropagation();removeRecentSearch(rb.getAttribute('data-remove'));}else{var chip=e.target.closest('.recent-search-chip');if(chip){var q=chip.getAttribute('data-query');if(q){document.getElementById('search-input').value=q;fetchTorrents(q,catMap[state.selectedCategory]||'0');}}}});
100  var th=document.querySelector('.results-table thead'); if(th)th.addEventListener('click',function(e){var s=e.target.closest('th.sortable');if(!s)return;var k=s.getAttribute('data-sort');if(state.sortBy===k)state.sortOrder=state.sortOrder==='asc'?'desc':'asc';else{state.sortBy=k;state.sortOrder='desc';}state.currentPage=1;renderResults();});
101  var pb=document.getElementById('prev-page-btn'),nb=document.getElementById('next-page-btn');
102  if(pb)pb.addEventListener('click',function(){if(state.currentPage>1){state.currentPage-=1;renderResults();document.getElementById('results-panel').scrollIntoView({behavior:'smooth'});}});
103  if(nb)nb.addEventListener('click',function(){var tp=Math.ceil(state.visibleResults.length/state.itemsPerPage)||
1031;if(state.currentPage<tp){state.currentPage+=1;renderResults();document.getElementById('results-panel').scrollIntoView({behavior:'smooth'});}});
104  document.querySelectorAll('.carousel-tab').forEach(tab=>tab.addEventListener('click',function(){switchCarouselTab(tab.getAttribute('data-tab'));}));
105  var cP=document.getElementById('carousel-prev'),cN=document.getElementById('carousel-next');
106  if(cP)cP.addEventListener('click',function(){carouselIndex=(carouselIndex>0)?carouselIndex-1:Math.max(carouselItems.length-1,0);updateCarousel();});
107  if(cN)cN.addEventListener('click',function(){if(!carouselItems.length)return;carouselIndex=(carouselIndex<carouselItems.length-1)?carouselIndex+1:0;updateCarousel();});
108}
109
110window.openTorrentDetails=openTorrentDetails; window.trackDownload=trackDownload;
111document.addEventListener('DOMContentLoaded',function(){
112    renderCategoryFilters();
113    renderQuickFilters();
114    loadRecentSearches();
115    attachHandlers();
116    loadCarouselData('movie');
117    var p=new URLSearchParams(window.location.search);
118    var q=p.get('q')||'';
119    var cat=p.get('category')||'0';
120    
121    // FIX: Completely removed the auto-search (fetchTorrents) call when on the home page.
122    // It will only search if a query 'q' is present in the URL.
123    if(q) {
124        var si=document.getElementById('search-input');
125        if(si) si.value=q;
126        fetchTorrents(q,cat);
127        window.history.replaceState({},'','/');
128    } else {
129        // Just hide the panel, do not fetch.
130        var panel = document.getElementById('results-panel');
131        if (panel) panel.classList.add('hidden');
132    }
133});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.