You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
ImagesDownloader/index.js

255 lines
9.5 KiB

class DownloadManager {
constructor() {
this.port = window.location.port || '55830';
this.baseUrl = `http://127.0.0.1:${this.port}/api`;
this.currentDownload = null;
this.init();
}
init() {
document.getElementById('port').textContent = this.port;
// 绑定事件
document.getElementById('reloadBtn').addEventListener('click', () => this.reloadFolders());
document.getElementById('cleanupBtn').addEventListener('click', () => this.cleanupJson());
document.getElementById('closeModalBtn').addEventListener('click', () => this.hideModal());
document.querySelector('.close-btn').addEventListener('click', () => this.hideModal());
// 点击模态框外部关闭
document.getElementById('downloadModal').addEventListener('click', (e) => {
if (e.target === document.getElementById('downloadModal')) {
this.hideModal();
}
});
// 初始加载
this.reloadFolders();
}
showStatus(message, type = 'info') {
const statusEl = document.getElementById('statusMessage');
statusEl.textContent = message;
statusEl.style.borderLeftColor = type === 'error' ? '#ef4444' :
type === 'warning' ? '#f59e0b' : '#10b981';
}
showModal(title) {
document.getElementById('modalTitle').textContent = title;
document.getElementById('downloadModal').style.display = 'flex';
this.resetModal();
}
hideModal() {
document.getElementById('downloadModal').style.display = 'none';
if (this.currentDownload && this.currentDownload.abort) {
this.currentDownload.abort();
}
}
resetModal() {
document.getElementById('progressBar').style.width = '0%';
document.getElementById('progressText').textContent = '0%';
document.getElementById('totalFiles').textContent = '0';
document.getElementById('downloadedFiles').textContent = '0';
document.getElementById('pendingFiles').textContent = '0';
document.getElementById('downloadLog').innerHTML = '';
}
updateProgress(progress, stats) {
document.getElementById('progressBar').style.width = `${progress}%`;
document.getElementById('progressText').textContent = `${Math.round(progress)}%`;
if (stats) {
document.getElementById('totalFiles').textContent = stats.total || 0;
document.getElementById('downloadedFiles').textContent = stats.downloaded || 0;
document.getElementById('pendingFiles').textContent = stats.pending || 0;
}
}
addLogEntry(message, type = 'info') {
const logEl = document.getElementById('downloadLog');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logEl.appendChild(entry);
logEl.scrollTop = logEl.scrollHeight;
}
async reloadFolders() {
try {
this.showStatus('正在加载文件夹...', 'info');
document.getElementById('reloadBtn').disabled = true;
const response = await fetch(`${this.baseUrl}/reload_folders`);
const data = await response.json();
if (data.success) {
this.displayFolders(data.folders);
this.showStatus(`已加载 ${data.folders.length} 个未完成的任务`, 'success');
} else {
throw new Error(data.message || '加载失败');
}
} catch (error) {
console.error('加载失败:', error);
this.showStatus(`加载失败: ${error.message}`, 'error');
this.displayFolders([]);
} finally {
document.getElementById('reloadBtn').disabled = false;
}
}
displayFolders(folders) {
const foldersList = document.getElementById('foldersList');
const noFolders = document.getElementById('noFolders');
// 过滤掉进度为100%的文件夹
const incompleteFolders = folders.filter(folder => folder.progress < 100);
if (incompleteFolders.length === 0) {
foldersList.innerHTML = '';
noFolders.style.display = 'block';
return;
}
noFolders.style.display = 'none';
foldersList.innerHTML = incompleteFolders.map(folder => `
<div class="folder-item">
<div class="folder-info">
<div class="folder-title">
<i class="fas fa-book"></i>
${this.escapeHtml(folder.title)}
</div>
<div class="folder-stats">
<span>总文件: ${folder.total}</span>
<span>已下载: ${folder.downloaded}</span>
<span>缺失: ${folder.total - folder.downloaded}</span>
</div>
<div class="progress-container">
<div class="progress-wrapper">
<div class="progress">
<div class="progress-fill" style="width: ${folder.progress}%"></div>
</div>
<div class="progress-text">${folder.progress.toFixed(1)}%</div>
</div>
</div>
</div>
<div class="folder-actions">
<button class="btn btn-primary btn-small" onclick="downloadManager.downloadFolder('${this.escapeAttr(folder.folder)}', '${this.escapeAttr(folder.title)}')">
<i class="fas fa-download"></i> 下载缺失文件 (${folder.total - folder.downloaded})
</button>
</div>
</div>
`).join('');
}
async downloadFolder(folderPath, title) {
try {
this.showModal(`正在下载: ${title}`);
this.addLogEntry('开始下载缺失文件...', 'info');
console.log('发送请求:', { folder: folderPath, title: title });
const response = await fetch(`${this.baseUrl}/download_missing`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
folder: folderPath,
title: title
})
});
// 检查响应状态
if (!response.ok) {
const errorText = await response.text();
console.error('HTTP错误:', response.status, errorText);
throw new Error(`HTTP ${response.status}: ${errorText}`);
}
const data = await response.json();
console.log('收到响应:', data);
if (data.success) {
// 更新进度
const totalSaved = (data.saved || 0) + (data.skipped || 0);
const total = data.total || 1;
const progress = totalSaved / total * 100;
this.updateProgress(progress, {
total: total,
downloaded: totalSaved,
pending: data.failed || 0
});
// 添加日志
if (data.details) {
data.details.forEach(detail => {
if (detail.status === 'success') {
this.addLogEntry(`成功下载: ${detail.key}`, 'success');
} else if (detail.status === 'failed') {
this.addLogEntry(`下载失败 ${detail.key}: ${detail.message}`, 'error');
} else if (detail.status === 'skipped') {
this.addLogEntry(`跳过: ${detail.key} (${detail.message})`, 'info');
}
});
}
this.addLogEntry(`下载完成: ${data.message}`, 'success');
// 刷新文件夹列表
setTimeout(() => this.reloadFolders(), 1000);
} else {
this.addLogEntry(`下载失败: ${data.message}`, 'error');
}
} catch (error) {
console.error('下载失败:', error);
this.addLogEntry(`下载失败: ${error.message}`, 'error');
}
}
async cleanupJson() {
if (!confirm('确定要删除所有JSON文件吗?此操作不可恢复。')) {
return;
}
try {
this.showStatus('正在清理JSON文件...', 'info');
document.getElementById('cleanupBtn').disabled = true;
const response = await fetch(`${this.baseUrl}/cleanup_json`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
this.showStatus(data.message, 'success');
// 刷新文件夹列表
setTimeout(() => this.reloadFolders(), 500);
} else {
throw new Error(data.message || '清理失败');
}
} catch (error) {
console.error('清理失败:', error);
this.showStatus(`清理失败: ${error.message}`, 'error');
} finally {
document.getElementById('cleanupBtn').disabled = false;
}
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
escapeAttr(text) {
return this.escapeHtml(text).replace(/"/g, '&quot;');
}
}
// 初始化应用
const downloadManager = new DownloadManager();