Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>RapidMetaSearch // High-Performance Crawler</title> | |
| <!-- Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --bg-dark: #0f1115; | |
| --bg-panel: #161b22; | |
| --bg-card: #1c2128; | |
| --accent-primary: #00f2ff; /* Cyan */ | |
| --accent-secondary: #7000ff; /* Purple */ | |
| --text-main: #e6edf3; | |
| --text-muted: #8b949e; | |
| --success: #2ea043; | |
| --danger: #f85149; | |
| --warning: #d29922; | |
| --font-main: 'Inter', system-ui, -apple-system, sans-serif; | |
| --font-mono: 'Fira Code', monospace; | |
| } | |
| * { | |
| box-sizing: box-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| background-color: --bg-dark; | |
| color: var(--text-main); | |
| font-family: var(--font-main); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| background: radial-gradient(circle at top right, #1a202c 0%, #0f1115 100%); | |
| } | |
| /* --- Header & Nav --- */ | |
| header { | |
| background: rgba(13, 17, 23, 0.8); | |
| border-bottom: 1px solid rgba(56, 139, 253, 0.1); | |
| padding: 1rem 2rem; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| backdrop-filter: blur(10px); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-weight: 700; | |
| font-size: 1.2rem; | |
| letter-spacing: -0.5px; | |
| } | |
| .brand i { | |
| color: var(--accent-primary); | |
| } | |
| .anycoder-link { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| border: 1px solid var(--text-muted); | |
| padding: 4px 12px; | |
| border-radius: 6px; | |
| transition: all 0.3s ease; | |
| } | |
| .anycoder-link:hover { | |
| border-color: var(--accent-primary); | |
| color: var(--accent-primary); | |
| box-shadow: 0 0 10px rgba(0, 242, 255, 0.2); | |
| } | |
| /* --- Main Layout --- */ | |
| main { | |
| flex: 1; | |
| padding: 2rem; | |
| max-width: 1400px; | |
| width: 100%; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 2rem; | |
| } | |
| /* --- Dashboard Grid --- */ | |
| .dashboard-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 300px; | |
| gap: 20px; | |
| } | |
| @media (max-width: 900px) { | |
| .dashboard-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| /* --- Search Section --- */ | |
| .search-panel { | |
| background: var(--bg-panel); | |
| border: 1px solid rgba(56, 139, 253, 0.1); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 1rem; | |
| } | |
| .keyword-input { | |
| flex: 1; | |
| background: #0d1117; | |
| border: 1px solid #30363d; | |
| color: var(--text-main); | |
| padding: 12px 16px; | |
| border-radius: 6px; | |
| font-family: var(--font-mono); | |
| transition: border-color 0.2s; | |
| } | |
| .keyword-input:focus { | |
| outline: none; | |
| border-color: var(--accent-primary); | |
| } | |
| .search-btn { | |
| background: linear-gradient(135deg, var(--accent-secondary), #5a00d6); | |
| color: white; | |
| border: none; | |
| padding: 12px 30px; | |
| border-radius: 6px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .search-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(112, 0, 255, 0.4); | |
| } | |
| .search-btn:disabled { | |
| opacity: 0.7; | |
| cursor: not-allowed; | |
| } | |
| /* --- Stats Panel --- */ | |
| .stats-panel { | |
| background: var(--bg-panel); | |
| border: 1px solid rgba(56, 139, 253, 0.1); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| .stat-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-size: 0.9rem; | |
| } | |
| .stat-value { | |
| font-family: var(--font-mono); | |
| font-weight: bold; | |
| color: var(--accent-primary); | |
| } | |
| .progress-bar-container { | |
| height: 4px; | |
| background: #30363d; | |
| border-radius: 2px; | |
| overflow: hidden; | |
| margin-top: 5px; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| width: 0%; | |
| background: var(--accent-primary); | |
| transition: width 0.3s ease; | |
| box-shadow: 0 0 10px var(--accent-primary); | |
| } | |
| /* --- Results Area --- */ | |
| .results-container { | |
| margin-top: 2rem; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |
| gap: 20px; | |
| } | |
| .result-card { | |
| background: var(--bg-card); | |
| border: 1px solid #30363d; | |
| border-radius: 8px; | |
| padding: 15px; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| animation: slideIn 0.4s ease forwards; | |
| } | |
| @keyframes slideIn { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .result-card:hover { | |
| border-color: var(--text-muted); | |
| transform: translateY(-3px); | |
| } | |
| .card-header { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 10px; | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| } | |
| .url-display { | |
| font-family: var(--font-mono); | |
| color: var(--text-main); | |
| font-size: 0.9rem; | |
| word-break: break-all; | |
| margin-bottom: 10px; | |
| display: block; | |
| } | |
| .status-badge { | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| font-size: 0.7rem; | |
| font-weight: bold; | |
| text-transform: uppercase; | |
| } | |
| .status-valid { background: rgba(46, 160, 67, 0.2); color: var(--success); } | |
| .status-dead { background: rgba(248, 81, 49, 0.2); color: var(--danger); } | |
| .status-pending { background: rgba(210, 153, 34, 0.2); color: var(--warning); } | |
| .meta-info { | |
| display: flex; | |
| justify-content: space-between; | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| border-top: 1px solid #30363d; | |
| padding-top: 10px; | |
| margin-top: 10px; | |
| } | |
| /* --- Terminal / Log View --- */ | |
| .terminal-view { | |
| margin-top: 2rem; | |
| background: #000; | |
| border: 1px solid #333; | |
| border-radius: 8px; | |
| padding: 1rem; | |
| font-family: var(--font-mono); | |
| font-size: 0.8rem; | |
| height: 200px; | |
| overflow-y: auto; | |
| color: #ccc; | |
| } | |
| .log-entry { | |
| margin-bottom: 4px; | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .log-time { color: var(--accent-secondary); } | |
| .log-msg { color: #ccc; } | |
| /* --- Utility Classes --- */ | |
| .hidden { display: none; } | |
| .text-accent { color: var(--accent-primary); } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="brand"> | |
| <i class="fas fa-network-wired"></i> | |
| <span>RapidMeta<span class="text-accent">Search</span></span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank"> | |
| Built with anycoder <i class="fas fa-external-link-alt"></i> | |
| </a> | |
| </header> | |
| <main> | |
| <div class="dashboard-grid"> | |
| <!-- Left: Search & Results --> | |
| <div class="search-panel"> | |
| <h2 style="margin-bottom: 1rem; font-size: 1.1rem;">Search Parameters</h2> | |
| <div class="input-group"> | |
| <input type="text" id="kw1" class="keyword-input" placeholder="Keyword 1 (e.g. Movie Name)"> | |
| <input type="text" id="kw2" class="keyword-input" placeholder="Keyword 2 (e.g. Resolution)"> | |
| <input type="text" id="kw3" class="keyword-input" placeholder="Keyword 3 (e.g. Release Year)"> | |
| </div> | |
| <button class="search-btn" onclick="initiateSearch()"> | |
| <i class="fas fa-search"></i> Initiate Scan | |
| </button> | |
| <div class="results-container" id="resultsArea"> | |
| <!-- Results injected here --> | |
| </div> | |
| </div> | |
| <!-- Right: Stats & System Status --> | |
| <div class="stats-panel"> | |
| <h3 style="margin-bottom: 1rem; font-size: 1rem;">System Status</h3> | |
| <div class="stat-item"> | |
| <span>Workers Active</span> | |
| <span class="stat-value" id="workerCount">0</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span>Queue Depth</span> | |
| <span class="stat-value" id="queueDepth">0</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span>Cached Items</span> | |
| <span class="stat-value" id="cacheSize">0</span> | |
| </div> | |
| <div style="margin-top: 15px;"> | |
| <div class="stat-item"> | |
| <span>Scan Progress</span> | |
| <span class="stat-value" id="progressText">0%</span> | |
| </div> | |
| <div class="progress-bar-container"> | |
| <div class="progress-bar" id="mainProgressBar"></div> | |
| </div> | |
| </div> | |
| <div style="margin-top: 15px; font-size: 0.8rem; color: var(--text-muted);"> | |
| <p><i class="fas fa-check-circle text-accent"></i> Async Engine Ready</p> | |
| <p><i class="fas fa-database text-accent"></i> Local SQLite Mock</p> | |
| <p><i class="fas fa-bolt text-accent"></i> Rate Limiter Active</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Terminal View for "Backend" feel --> | |
| <div class="terminal-view" id="terminal"> | |
| <div class="log-entry"><span class="log-time">[SYSTEM]</span><span class="log-msg">Initialization complete. Waiting for input...</span></div> | |
| </div> | |
| </main> | |
| <script> | |
| // --- Architecture Constants & State --- | |
| const CONFIG = { | |
| maxWorkers: 5, | |
| requestDelay: 300, // Simulated network latency | |
| cache: new Set(), | |
| queue: [] | |
| }; | |
| let state = { | |
| isScanning: false, | |
| results: [], | |
| processed: 0, | |
| total: 0 | |
| }; | |
| // --- Logging System --- | |
| function log(msg, type = 'INFO') { | |
| const terminal = document.getElementById('terminal'); | |
| const time = new Date().toLocaleTimeString('en-GB'); | |
| const color = type === 'ERR' ? 'var(--danger)' : type === 'SUCCESS' ? 'var(--success)' : 'var(--accent-secondary)'; | |
| const entry = document.createElement('div'); | |
| entry.className = 'log-entry'; | |
| entry.innerHTML = `<span class="log-time" style="color:${color}">[${time}] ${type}</span><span class="log-msg">${msg}</span>`; | |
| terminal.appendChild(entry); | |
| terminal.scrollTop = terminal.scrollHeight; | |
| } | |
| // --- Mock Backend: Search Engine Adapter Layer --- | |
| // Simulates fetching raw URLs from search engines based on keywords | |
| async function mockSearchEngineAdapter(kw1, kw2, kw3) { | |
| const base = "https://rapidgator.net/file/"; | |
| const hashLength = 12; | |
| const results = []; | |
| // Generate 10-20 "potential" links based on inputs | |
| const count = Math.floor(Math.random() * 10) + 10; | |
| for(let i=0; i<count; i++) { | |
| const hash = Math.random().toString(36).substring(2, 14) + Math.random().toString(36).substring(2, 14); | |
| const fileName = `${kw1 || 'Data'}_${kw2 || 'Archive'}_${kw3 || 'Part'}_${i}.zip`; | |
| results.push({ | |
| url: `${base}${hash}`, | |
| filename: fileName, | |
| source: 'Index-Alpha' | |
| }); | |
| } | |
| return results; | |
| } | |
| // --- Mock Backend: Availability Checker --- | |
| // Simulates HTTP status check and deduplication | |
| async function mockAvailabilityChecker(item) { | |
| // Simulate network request time | |
| await new Promise(r => setTimeout(r, CONFIG.requestDelay)); | |
| // Deduplication Check | |
| if (CONFIG.cache.has(item.url)) { | |
| return { ...item, status: 'duplicate', http: 200 }; | |
| } | |
| CONFIG.cache.add(item.url); | |
| // Simulate Availability (70% chance valid) | |
| const isAvailable = Math.random() > 0.3; | |
| return { | |
| ...item, | |
| status: isAvailable ? 'valid' : 'dead', | |
| http: isAvailable ? 200 : 404, | |
| size: isAvailable ? (Math.random() * 2 + 0.5).toFixed(2) + ' GB' : '0 KB', | |
| date: new Date().toISOString().split('T')[0] | |
| }; | |
| } | |
| // --- Async Crawling Engine --- | |
| async function processQueue() { | |
| const workers = []; | |
| while(CONFIG.queue.length > 0 || workers.length > 0) { | |
| // Update Stats | |
| document.getElementById('queueDepth').innerText = CONFIG.queue.length; | |
| document.getElementById('workerCount').innerText = workers.length; | |
| // Fill workers | |
| while(workers.length < CONFIG.maxWorkers && CONFIG.queue.length > 0) { | |
| const task = CONFIG.queue.pop(); | |
| const workerPromise = mockAvailabilityChecker(task).then(result => { | |
| state.processed++; | |
| updateProgress(); | |
| if(result.status !== 'duplicate') { | |
| renderResult(result); | |
| log(`Checked: ${result.filename} -> ${result.status.toUpperCase()}`, result.status === 'valid' ? 'SUCCESS' : 'WARN'); | |
| } | |
| }); | |
| workers.push(workerPromise); | |
| } | |
| // Wait for at least one worker to finish if queue is empty but workers active | |
| if(workers.length > 0) { | |
| await Promise.all(workers); | |
| workers = []; // Clear workers for next batch | |
| } | |
| } | |
| state.isScanning = false; | |
| document.getElementById('workerCount').innerText = 0; | |
| log("Scan complete. Queue empty.", "DONE"); | |
| document.querySelector('.search-btn').disabled = false; | |
| } | |
| // --- UI Functions --- | |
| function updateProgress() { | |
| const pct = Math.min(100, Math.round((state.processed / state.total) * 100)); | |
| document.getElementById('mainProgressBar').style.width = `${pct}%`; | |
| document.getElementById('progressText').innerText = `${pct}%`; | |
| document.getElementById('cacheSize').innerText = CONFIG.cache.size; | |
| } | |
| function renderResult(data) { | |
| const container = document.getElementById('resultsArea'); | |
| const card = document.createElement('div'); | |
| card.className = 'result-card'; | |
| const badgeClass = data.status === 'valid' ? 'status-valid' : 'status-dead'; | |
| const badgeText = data.status === 'valid' ? 'Active' : 'Offline'; | |
| card.innerHTML = ` | |
| <div class="card-header"> | |
| <span>${data.source}</span> | |
| <span class="status-badge ${badgeClass}">${badgeText}</span> | |
| </div> | |
| <div class="url-display">${data.url}</div> | |
| <div class="meta-info"> | |
| <span>${data.filename}</span> | |
| <span>${data.size}</span> | |
| </div> | |
| `; | |
| container.appendChild(card); | |
| } | |
| async function initiateSearch() { | |
| if(state.isScanning) return; | |
| const kw1 = document.getElementById('kw1').value; | |
| const kw2 = document.getElementById('kw2').value; | |
| const kw3 = document.getElementById('kw3').value; | |
| if(!kw1 && !kw2 && !kw3) { | |
| log("Error: Please provide at least one keyword.", "ERR"); | |
| return; | |
| } | |
| // Reset UI | |
| document.getElementById('resultsArea').innerHTML = ''; | |
| document.querySelector('.search-btn').disabled = true; | |
| state.isScanning = true; | |
| state.processed = 0; | |
| CONFIG.queue = []; // Reset queue | |
| log(`Initializing Search Engine Adapter for: "${kw1} ${kw2} ${kw3}"...`); | |
| // 1. Fetch Raw Data | |
| const rawLinks = await mockSearchEngineAdapter(kw1, kw2, kw3); | |
| state.total = rawLinks.length; | |
| log(`Found ${rawLinks.length} potential references. Starting Async Crawler.`); | |
| // 2. Populate Queue | |
| CONFIG.queue = [...rawLinks]; | |
| // 3. Start Engine | |
| processQueue(); | |
| } | |
| </script> | |
| </body> | |
| </html> |