const statusText = document.getElementById('status-text'); const wsIndicator = document.getElementById('ws-indicator'); const searchBtn = document.getElementById('search-btn'); const searchInput = document.getElementById('search-input'); const resultsContainer = document.getElementById('discovery-results'); const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('file-input'); const dlBtn = document.getElementById('dl-btn'); const dlUrl = document.getElementById('dl-url'); const dlStart = document.getElementById('dl-start'); const dlEnd = document.getElementById('dl-end'); const confirmModal = document.getElementById('confirm-modal'); const dlConfirmBtn = document.getElementById('dl-confirm-btn'); const dlCancelBtn = document.getElementById('dl-cancel-btn'); // --- WebSocket --- let ws; function connectWS() { const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'; const wsUrl = `${proto}://${window.location.host}/ws`; // For local dev without docker, port might be 3000 const finalWsUrl = window.location.port === '' ? `${proto}://${window.location.hostname}:3000/ws` : wsUrl; ws = new WebSocket(finalWsUrl); ws.onopen = () => { wsIndicator.className = 'h-2 w-2 rounded-full bg-green-500'; }; ws.onmessage = (event) => { statusText.textContent = event.data; // flash text statusText.classList.add('text-blue-400'); setTimeout(() => statusText.classList.remove('text-blue-400'), 500); }; ws.onclose = () => { wsIndicator.className = 'h-2 w-2 rounded-full bg-red-500'; setTimeout(connectWS, 2000); // Reconnect }; } connectWS(); // --- Zone A: Search --- searchBtn.addEventListener('click', async () => { const query = searchInput.value.trim(); if (!query) return; searchBtn.disabled = true; searchBtn.textContent = '검색 중...'; resultsContainer.innerHTML = '
인공지능이 이미지를 탐색하고 선별 중입니다...
'; try { const port = window.location.port ? `:${window.location.port}` : ':3000'; const res = await fetch(`http://${window.location.hostname}${port}/api/search?q=${encodeURIComponent(query)}`); const data = await res.json(); if (data.error) { resultsContainer.innerHTML = `
Error: ${data.error}
`; } else if (data.recommended && data.recommended.length > 0) { resultsContainer.innerHTML = ''; data.recommended.forEach((item, index) => { const delay = index * 100; resultsContainer.innerHTML += `
✨ AI Recommended

${item.reason}

`; }); } else { resultsContainer.innerHTML = `
결과를 찾을 수 없습니다.
`; } } catch (err) { resultsContainer.innerHTML = `
Exception: ${err.message}
`; } finally { searchBtn.disabled = false; searchBtn.textContent = '검색'; } }); // --- Zone B: Upload --- dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('border-purple-500', 'bg-[#303030]'); }); dropzone.addEventListener('dragleave', () => { dropzone.classList.remove('border-purple-500', 'bg-[#303030]'); }); dropzone.addEventListener('drop', (e) => { e.preventDefault(); dropzone.classList.remove('border-purple-500', 'bg-[#303030]'); if (e.dataTransfer.files.length > 0) { uploadFile(e.dataTransfer.files[0]); } }); dropzone.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { uploadFile(e.target.files[0]); } }); async function uploadFile(file) { const formData = new FormData(); formData.append('file', file); statusText.textContent = `Uploading ${file.name}...`; try { const port = window.location.port ? `:${window.location.port}` : ':3000'; const res = await fetch(`http://${window.location.hostname}${port}/api/upload`, { method: 'POST', body: formData }); const data = await res.json(); if (data.error) { statusText.textContent = `Upload Error: ${data.error}`; } else { statusText.textContent = `Upload Success: ${data.filename}`; } } catch (err) { statusText.textContent = `Upload Failed: ${err.message}`; } } // --- Zone C: Download --- async function requestDownload(confirm = false) { const url = dlUrl.value.trim(); if (!url) return; dlBtn.disabled = true; dlBtn.textContent = '요청 중...'; const payload = { url: url, start: dlStart.value.trim(), end: dlEnd.value.trim() }; try { const port = window.location.port ? `:${window.location.port}` : ':3000'; const res = await fetch(`http://${window.location.hostname}${port}/api/download${confirm ? '?confirm=true' : ''}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (res.status === 409) { // Duplicate! confirmModal.classList.remove('hidden'); confirmModal.classList.add('flex'); dlBtn.disabled = false; dlBtn.textContent = '다운로드 시작'; return; } const data = await res.json(); if (data.error) { statusText.textContent = `Download Error: ${data.error}`; } else { // The WS will handle the progress dlUrl.value = ''; dlStart.value = ''; dlEnd.value = ''; confirmModal.classList.add('hidden'); confirmModal.classList.remove('flex'); } } catch (err) { statusText.textContent = `Download Request Failed: ${err.message}`; } finally { dlBtn.disabled = false; dlBtn.textContent = '다운로드 시작'; } } dlBtn.addEventListener('click', () => requestDownload(false)); dlConfirmBtn.addEventListener('click', () => requestDownload(true)); dlCancelBtn.addEventListener('click', () => { confirmModal.classList.add('hidden'); confirmModal.classList.remove('flex'); });