function sendMessageToBackground(message) { return new Promise(function(resolve, reject) { chrome.runtime.sendMessage(message, function(response) { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } resolve(response || null); }); }); } function setMessage(text, isError) { var message = document.getElementById('message'); if (!message) return; message.textContent = text || ''; message.style.color = isError ? '#ff8585' : '#8892a4'; } var lastGoodCloudStats = null; var lastGoodSyncStatus = null; var syncProgressTimer = null; var lastServerHealthy = null; function wait(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }); } function setSyncProgress(text) { var el = document.getElementById('syncProgressText'); if (!el) return; el.textContent = text || ''; } function startSyncProgress(prefix) { var startedAt = Date.now(); var dots = 0; if (syncProgressTimer) { clearInterval(syncProgressTimer); } setSyncProgress((prefix || '正在同步') + ' 0s'); syncProgressTimer = setInterval(function() { dots = (dots + 1) % 4; var suffix = new Array(dots + 1).join('.'); var elapsed = Math.max(0, Math.floor((Date.now() - startedAt) / 1000)); setSyncProgress((prefix || '正在同步') + suffix + ' ' + elapsed + 's'); }, 500); } function stopSyncProgress(text) { if (syncProgressTimer) { clearInterval(syncProgressTimer); syncProgressTimer = null; } setSyncProgress(text || ''); } function updateStatus(status) { if (status && status.authenticated !== undefined) { lastGoodSyncStatus = status; } var accountDot = document.getElementById('accountDot'); var accountText = document.getElementById('accountText'); var email = document.getElementById('syncEmail'); var loggedOut = document.getElementById('authLoggedOut'); var loggedIn = document.getElementById('authLoggedIn'); var loggedInEmail = document.getElementById('loggedInEmail'); var healthy = !!(status && status.color === 'green'); var authenticated = !!(status && status.authenticated); if (accountDot) accountDot.classList.toggle('ok', healthy); if (accountText) accountText.textContent = status && (status.accountText || status.text) ? (status.accountText || status.text) : '账号状态未知'; if (email) email.textContent = status && status.email ? ('账号:' + status.email) : ''; if (loggedOut) loggedOut.classList.toggle('hidden', authenticated); if (loggedIn) loggedIn.classList.toggle('hidden', !authenticated); if (loggedInEmail) loggedInEmail.textContent = status && status.email ? ('当前账号:' + status.email) : '当前账号:未知'; } function updateServerStatus(ok, text) { var serverDot = document.getElementById('serverDot'); var serverText = document.getElementById('serverText'); lastServerHealthy = !!ok; if (serverDot) serverDot.classList.toggle('ok', !!ok); if (serverText) serverText.textContent = text || (ok ? '服务器状态正常' : '服务器状态异常'); } function formatDateTime(value) { if (!value) return '暂无'; try { var date = new Date(value); if (Number.isNaN(date.getTime())) return String(value); return date.toLocaleString('zh-CN'); } catch (error) { return String(value); } } function renderCloudStats(stats) { var summary = document.getElementById('cloudStatsSummary'); var latest = document.getElementById('cloudStatsLatest'); var tables = document.getElementById('cloudStatsTables'); if (!summary || !latest || !tables) return; if (!stats || !stats.ok) { if (lastGoodCloudStats && lastGoodCloudStats.ok) { updateServerStatus(true, '服务器状态正常(展示最近一次成功数据)'); summary.textContent = '云端状态暂时刷新失败,显示最近一次成功数据'; latest.textContent = '最近成功更新:线程 ' + formatDateTime(lastGoodCloudStats.latest.threads) + ' | 范围 ' + formatDateTime(lastGoodCloudStats.latest.coverages) + ' | 页缓存 ' + formatDateTime(lastGoodCloudStats.latest.pages); tables.textContent = '上次空间占用:' + (Array.isArray(lastGoodCloudStats.tables) ? lastGoodCloudStats.tables.map(function(table) { return table.tableName + ' ' + table.sizeMb + 'MB'; }).join(' | ') : '暂无'); return; } updateServerStatus(false, '服务器状态暂时不可读'); summary.textContent = '暂时无法读取云端状态'; latest.textContent = ''; tables.textContent = ''; return; } updateServerStatus(true, '服务器状态正常'); lastGoodCloudStats = stats; summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 用户 ' + stats.counts.users + ' 个 / 保险柜 ' + stats.counts.vaultItems + ' 条'; latest.textContent = '最近更新:线程 ' + formatDateTime(stats.latest.threads) + ' | 范围 ' + formatDateTime(stats.latest.coverages) + ' | 页缓存 ' + formatDateTime(stats.latest.pages); tables.textContent = '空间占用:' + (Array.isArray(stats.tables) ? stats.tables.map(function(table) { return table.tableName + ' ' + table.sizeMb + 'MB'; }).join(' | ') : '暂无'); } function formatBytes(bytes) { var value = Number(bytes || 0); if (!Number.isFinite(value) || value <= 0) return '0 B'; if (value < 1024) return value + ' B'; if (value < 1024 * 1024) return (value / 1024).toFixed(1) + ' KB'; if (value < 1024 * 1024 * 1024) return (value / 1024 / 1024).toFixed(2) + ' MB'; return (value / 1024 / 1024 / 1024).toFixed(2) + ' GB'; } function renderLocalStats(stats) { var summary = document.getElementById('localStatsSummary'); var storage = document.getElementById('localStatsStorage'); var meta = document.getElementById('localStatsMeta'); if (!summary || !storage || !meta) return; if (!stats || !stats.ok) { summary.textContent = '暂时无法读取本地状态'; storage.textContent = ''; meta.textContent = ''; return; } summary.textContent = '帖子索引 ' + stats.counts.threads + ' 条(含磁力 ' + (stats.counts.magnetThreads || 0) + ' 条)/ 页缓存 ' + stats.counts.pages + ' 条 / 范围缓存 ' + stats.counts.coverages + ' 条 / 收藏 ' + stats.counts.favorites + ' 条 / 历史 ' + stats.counts.history + ' 条'; storage.textContent = '本地存储占用:' + formatBytes(stats.storage.usage) + ' / ' + formatBytes(stats.storage.quota); meta.textContent = '上传抑制元数据:线程 ' + stats.counts.uploadMetaThreads + ' 条 | 范围 ' + stats.counts.uploadMetaCoverages + ' 条 | 页 ' + stats.counts.uploadMetaPages + ' 条'; } async function refreshStatus() { try { var response = await sendMessageToBackground({ action: 'cloudGetSyncStatus' }); if (response && response.ok !== false) { updateStatus(response || { color: 'red', text: '状态未知', authenticated: false }); return; } if (lastGoodSyncStatus) { updateStatus(lastGoodSyncStatus); setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true); return; } updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' }); } catch (error) { if (lastGoodSyncStatus) { updateStatus(lastGoodSyncStatus); setMessage('云同步状态暂时刷新失败,显示最近一次成功状态', true); return; } updateStatus({ color: 'red', text: '云同步异常', authenticated: false, email: '' }); setMessage('状态获取失败:' + error.message, true); } } async function refreshCloudStats() { var attempts = 0; var response = null; var error = null; for (attempts = 0; attempts < 3; attempts++) { try { response = await sendMessageToBackground({ action: 'cloudGetCacheStats' }); if (response && response.ok) { renderCloudStats(response); return; } } catch (err) { error = err; } await wait(500 * (attempts + 1)); } renderCloudStats(null); if (error) { setMessage('云端状态暂时刷新失败,稍后会自动重试', true); } } async function refreshLocalStats() { try { var response = await sendMessageToBackground({ action: 'localGetCacheStats' }); renderLocalStats(response); } catch (error) { renderLocalStats(null); } } async function submitAuth(action) { var emailInput = document.getElementById('emailInput'); var passwordInput = document.getElementById('passwordInput'); var email = emailInput ? emailInput.value.trim() : ''; var password = passwordInput ? passwordInput.value : ''; if (!email || !password) { setMessage('请输入邮箱和密码', true); return; } setMessage(action === 'cloudRegister' ? '正在注册...' : '正在登录...', false); try { var response = await sendMessageToBackground({ action: action, email: email, password: password }); if (!response || !response.ok) { setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + (response && response.error ? response.error : '未知错误'), true); return; } if (passwordInput) passwordInput.value = ''; updateStatus(response.status || { color: 'green', text: '云同步正常', authenticated: true, email: email }); setMessage(action === 'cloudRegister' ? '注册成功,云同步已开启' : '登录成功,云同步已开启', false); } catch (error) { setMessage((action === 'cloudRegister' ? '注册失败:' : '登录失败:') + error.message, true); } } async function syncNow() { setMessage('', false); startSyncProgress('正在同步本地缓存到云端'); try { await sendMessageToBackground({ action: 'cloudBackfillLocalCache', force: true }); stopSyncProgress('已提交同步任务,正在刷新状态...'); await wait(1200); startSyncProgress('正在刷新云端状态'); await refreshStatus(); await refreshCloudStats(); await refreshLocalStats(); stopSyncProgress('同步完成'); setMessage('已触发云端回填', false); } catch (error) { stopSyncProgress('同步失败'); setMessage('同步失败:' + error.message, true); } } async function logout() { try { await sendMessageToBackground({ action: 'cloudLogout' }); await refreshStatus(); setMessage('已退出登录', false); } catch (error) { setMessage('退出失败:' + error.message, true); } } function openExternalUrl(url) { chrome.tabs.create({ url: url }); } document.addEventListener('DOMContentLoaded', function() { document.getElementById('loginBtn').addEventListener('click', function() { submitAuth('cloudLogin'); }); document.getElementById('registerBtn').addEventListener('click', function() { submitAuth('cloudRegister'); }); document.getElementById('syncNowBtn').addEventListener('click', syncNow); document.getElementById('logoutBtn').addEventListener('click', logout); document.getElementById('downloadNode2Btn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/qBittorrent_5.1.4_x64_setup.exe'); }); document.getElementById('downloadOfficialBtn').addEventListener('click', function() { openExternalUrl('https://sourceforge.net/projects/qbittorrent/files/qbittorrent-win32/qbittorrent-5.1.4/qbittorrent_5.1.4_x64_setup.exe/download'); }); document.getElementById('downloadMotrixBtn').addEventListener('click', function() { openExternalUrl('http://7.haory.cn/x/x72/Motrix-Setup-1.8.19.exe'); }); refreshStatus(); refreshCloudStats(); refreshLocalStats(); });