class DownloadTool { constructor() { this.form = document.getElementById('downloadForm'); this.output = document.getElementById('output'); this.loadUrlsBtn = document.getElementById('loadUrls'); this.urlListTextarea = document.getElementById('urlList'); this.downloadUrlBtn = document.getElementById('downloadUrl'); this.cleanFilesBtn = document.getElementById('cleanFiles'); this.downloadImageBtn = document.getElementById('downloadImage'); this.checkIncompleteBtn = document.getElementById('checkIncomplete'); this.clearOutputBtn = document.getElementById('clearOutput'); this.proxySelect = document.getElementById('proxy'); this.websocket = null; this.isConnected = false; this.initEvents(); this.connectWebSocket(); } initEvents() { // 读取URL按钮 this.loadUrlsBtn.addEventListener('click', () => { this.loadTargetUrls(); }); // 下载URL按钮 this.downloadUrlBtn.addEventListener('click', () => { this.downloadUrls() }); // 下载图片按钮 this.downloadImageBtn.addEventListener('click', () => { this.downloadImages() }); // 检查未完成按钮 this.checkIncompleteBtn.addEventListener('click', () => { this.checkIncomplete(); }); // 清理文件按钮 this.cleanFilesBtn.addEventListener('click', () => { this.cleanFiles(); }); // 清除输出按钮 this.clearOutputBtn.addEventListener('click', () => { this.clearOutput(); }); } connectWebSocket() { try { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws`; this.websocket = new WebSocket(wsUrl); this.websocket.onopen = () => { this.isConnected = true; this.showOutput('WebSocket连接已建立,可以接收实时日志', 'success'); console.log('WebSocket连接已建立'); }; this.websocket.onmessage = (event) => { try { const logEntry = JSON.parse(event.data); this.appendRealtimeLog(logEntry); } catch (e) { console.error('解析WebSocket消息失败:', e); } }; this.websocket.onclose = () => { this.isConnected = false; this.showOutput('WebSocket连接已断开,正在尝试重连...', 'error'); console.log('WebSocket连接已断开'); // 5秒后尝试重连 setTimeout(() => this.connectWebSocket(), 5000); }; this.websocket.onerror = (error) => { console.error('WebSocket错误:', error); this.showOutput('WebSocket连接错误', 'error'); }; } catch (error) { console.error('创建WebSocket连接失败:', error); this.showOutput('WebSocket连接失败', 'error'); } } appendRealtimeLog(logEntry) { const timestamp = logEntry.time || new Date().toLocaleTimeString(); const level = logEntry.level || 'INFO'; const source = logEntry.source || 'system'; const message = logEntry.message || ''; const logLine = `[${timestamp}] [${level}] [${source}] ${message}`; // 追加到输出框 if (this.output.textContent) { this.output.textContent += '\n' + logLine; } else { this.output.textContent = logLine; } // 自动滚动到底部 this.output.scrollTop = this.output.scrollHeight; // 根据日志级别设置样式 if (level === 'ERROR') { this.output.classList.add('error'); } else if (level === 'SUCCESS') { this.output.classList.add('success'); } else { this.output.classList.remove('error', 'success'); } } async loadTargetUrls() { try { this.setLoading(true); this.showOutput('正在读取 targets.txt...', 'info'); const response = await fetch('/load_urls', { method: 'POST' }); const result = await response.json(); if (result.success) { // 在URL列表文本框中显示读取的URL this.urlListTextarea.value = result.urls.join('\n'); this.showOutput(`成功读取 ${result.urls.length} 个URL\n\nURL列表:\n${result.urls.join('\n')}`, 'success'); } else { this.showOutput(`读取失败: ${result.message}`, 'error'); } } catch (error) { this.showOutput(`读取URL时出错: ${error.message}`, 'error'); } finally { this.setLoading(false); } } async clearOutput() { try { const response = await fetch('/clear', { method: 'POST' }); const result = await response.json(); if (result.success) { this.showOutput('', 'success'); this.urlListTextarea.value = ''; // 同时清空URL列表 } } catch (error) { this.showOutput(`清除失败: ${error.message}`, 'error'); } } async downloadUrls() { try { const proxy = this.proxySelect.value; this.showOutput(`正在抓取画廊链接...\n代理: ${proxy}\n\n注意:此操作可能需要较长时间,请耐心等待...`, 'info'); // 使用setTimeout确保UI不被阻塞 setTimeout(async () => { try { const res = await fetch('/download_urls', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ proxy }) }); const data = await res.json(); this.showOutput(data.message, data.success ? 'success' : 'error'); } catch (error) { this.showOutput(`抓取画廊链接时出错: ${error.message}`, 'error'); } }, 100); } catch (error) { this.showOutput(`抓取画廊链接时出错: ${error.message}`, 'error'); } } async downloadImages() { try { const proxy = this.proxySelect.value; this.showOutput(`正在下载图片...\n代理: ${proxy}\n\n注意:此操作可能需要较长时间,请耐心等待...`, 'info'); // 使用setTimeout确保UI不被阻塞 setTimeout(async () => { try { const res = await fetch('/download_images', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ proxy }) }); const data = await res.json(); this.showOutput(data.message, data.success ? 'success' : 'error'); } catch (error) { this.showOutput(`下载图片时出错: ${error.message}`, 'error'); } }, 100); } catch (error) { this.showOutput(`下载图片时出错: ${error.message}`, 'error'); } } async checkIncomplete() { try { this.setLoading(true); this.showOutput('正在检查未完成文件...', 'info'); const response = await fetch('/check_incomplete', { method: 'POST' }); const result = await response.json(); if (result.success) { let message = `检查完成!\n\n`; message += `${result.data}`; this.showOutput(message, 'success'); } else { this.showOutput(`检查失败: ${result.message}`, 'error'); } } catch (error) { this.showOutput(`检查未完成文件时出错: ${error.message}`, 'error'); } finally { this.setLoading(false); } } async cleanFiles() { try { this.setLoading(true); this.showOutput('正在清理日志和JSON文件...', 'info'); const response = await fetch('/clean_files', { method: 'POST' }); const result = await response.json(); if (result.success) { let message = `清理完成!成功删除 ${result.deleted_count} 个文件\n\n`; if (result.deleted_files && result.deleted_files.length > 0) { message += "已删除的文件:\n" + result.deleted_files.join('\n'); } this.showOutput(message, 'success'); } else { let message = `清理完成,但有 ${result.error_count} 个文件删除失败\n\n`; if (result.deleted_files && result.deleted_files.length > 0) { message += "已删除的文件:\n" + result.deleted_files.join('\n') + '\n\n'; } if (result.error_files && result.error_files.length > 0) { message += "删除失败的文件:\n" + result.error_files.join('\n'); } this.showOutput(message, 'error'); } } catch (error) { this.showOutput(`清理文件时出错: ${error.message}`, 'error'); } finally { this.setLoading(false); } } showOutput(message, type = '') { this.output.textContent = message; this.output.className = 'output-area'; if (type) { this.output.classList.add(type); } // 自动滚动到底部 this.output.scrollTop = this.output.scrollHeight; } setLoading(loading) { const buttons = this.form.querySelectorAll('button'); buttons.forEach(button => { button.disabled = loading; }); if (loading) { document.body.classList.add('loading'); } else { document.body.classList.remove('loading'); } } } // 初始化应用 document.addEventListener('DOMContentLoaded', () => { new DownloadTool(); });