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 += `
`;
});
} 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');
});