';
document.body.appendChild(panel);
setPanelView('results');
ball.onclick = function() {
panel.style.display = 'flex';
ball.style.display = 'none';
};
var resultsSwitch = panel.querySelector('#magnet-view-results');
if (resultsSwitch) {
resultsSwitch.onclick = function() {
setPanelView('results');
};
}
var cacheSwitch = panel.querySelector('#magnet-view-cache');
if (cacheSwitch) {
cacheSwitch.onclick = function() {
setPanelView('cache');
refreshCacheOverview({ showStatus: true });
};
}
var refreshCacheBtn = panel.querySelector('#magnet-refresh-cache');
if (refreshCacheBtn) {
refreshCacheBtn.onclick = function() {
refreshCacheOverview({ showStatus: true });
};
}
var clearCacheInlineBtn = panel.querySelector('#magnet-clear-cache-inline');
if (clearCacheInlineBtn) {
clearCacheInlineBtn.onclick = clearAllCacheWithConfirm;
}
var closeBtn = panel.querySelector('.magnet-panel-close');
if (closeBtn) closeBtn.onclick = function() {
panel.style.display = 'none';
ball.style.display = 'flex';
};
var favoritesSwitch = panel.querySelector('#magnet-view-favorites');
if (favoritesSwitch) {
favoritesSwitch.onclick = function() {
setPanelView('favorites');
};
}
var clearFavoritesBtn = panel.querySelector('#magnet-clear-favorites');
if (clearFavoritesBtn) {
clearFavoritesBtn.onclick = function() {
if (confirm('确定要清空所有收藏吗')) {
saveFavorites([]);
renderFavoritesList();
}
};
}
var copyAllBtn = panel.querySelector('#magnet-copy-all');
if (copyAllBtn) {
copyAllBtn.onclick = function() {
var links = allMagnetLinks.length > 0
? allMagnetLinks.slice()
: Array.from(document.querySelectorAll('.magnet-item .magnet-copy-btn'))
.map(function(btn) { return btn.getAttribute('data-magnet'); })
.filter(function(link) { return !!link; });
if (links.length === 0) {
alert('暂无可复制的磁力链接');
return;
}
var allLinks = links.join('\n');
navigator.clipboard.writeText(allLinks)
.then(function() {
alert('已复制 ' + links.length + ' 个磁力链接!');
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
alert('复制失败: ' + errorMsg);
});
};
}
return panel;
var copyAllBtn = panel.querySelector('#magnet-copy-all');
if (copyAllBtn) {
copyAllBtn.onclick = function() {
var links = allMagnetLinks.length > 0
? allMagnetLinks.slice()
: Array.from(document.querySelectorAll('.magnet-item .magnet-copy-btn'))
.map(function(btn) { return btn.getAttribute('data-magnet'); })
.filter(function(link) { return !!link; });
if (links.length === 0) {
alert('暂无可复制的磁力链接');
return;
}
var allLinks = links.join('\n');
navigator.clipboard.writeText(allLinks)
.then(function() {
alert('已复制 ' + links.length + ' 个磁力链接!');
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
alert('复制失败:' + errorMsg);
});
};
}
return panel;
}
function isListPage() {
return document.querySelector('#threadlisttableid') !== null;
}
function getCurrentPage() {
var match = window.location.href.match(/[?&]page=(\d+)/);
if (match) return parseInt(match[1]);
match = window.location.href.match(/forum-\d+-(\d+)\.html/);
if (match) return parseInt(match[1]);
return 1;
}
function getForumOrigin(rawUrl) {
var sourceUrl = typeof rawUrl === 'string' && rawUrl ? rawUrl : window.location.href;
var match = sourceUrl.match(/^(https?:\/\/[^\/]+)\/?/i);
if (match) return match[1];
return window.location.origin || 'https://www.sehuatang.net';
}
function getForumIdFromUrl(rawUrl) {
var sourceUrl = typeof rawUrl === 'string' && rawUrl ? rawUrl : window.location.href;
var match = sourceUrl.match(/[?&]fid=(\d+)/i);
if (match) return match[1];
match = sourceUrl.match(/forum-(\d+)(?:-|\.html)/i);
if (match) return match[1];
return '2';
}
function getForumKey() {
return getForumOrigin(window.location.href) + '|fid:' + getForumIdFromUrl(window.location.href);
}
function getBaseUrl() {
return getForumOrigin(window.location.href) + '/forum-' + getForumIdFromUrl(window.location.href) + '-';
}
function normalizeThreadUrl(url) {
if (typeof url !== 'string' || !url) {
return '';
}
try {
var parsed = new URL(url, window.location.origin);
parsed.hash = '';
return parsed.href;
} catch (e) {
return url;
}
}
function getThreadKeyFromUrl(url) {
var normalizedUrl = normalizeThreadUrl(url);
if (!normalizedUrl) {
return '';
}
var match = normalizedUrl.match(/thread-(\d+)-/i) || normalizedUrl.match(/[?&]tid=(\d+)/i);
return match ? match[1] : normalizedUrl;
}
function normalizeCachedThreads(threads) {
var result = [];
var seen = Object.create(null);
(Array.isArray(threads) ? threads : []).forEach(function(thread) {
if (!thread || typeof thread !== 'object') return;
var normalizedUrl = normalizeThreadUrl(thread.url);
var normalizedTitle = typeof thread.title === 'string'
? thread.title.replace(/\s+/g, ' ').trim()
: '';
var threadKey = typeof thread.threadKey === 'string' && thread.threadKey
? thread.threadKey
: getThreadKeyFromUrl(normalizedUrl);
if (!normalizedUrl || !threadKey || seen[threadKey]) return;
seen[threadKey] = true;
result.push({
url: normalizedUrl,
title: normalizedTitle,
threadKey: threadKey
});
});
return result;
}
function normalizeMagnetList(magnets) {
var seen = Object.create(null);
var result = [];
(Array.isArray(magnets) ? magnets : []).forEach(function(magnet) {
if (typeof magnet !== 'string' || !magnet) return;
if (seen[magnet]) return;
seen[magnet] = true;
result.push(magnet);
});
return result;
}
function extractThreadsFromHtml(html) {
var parser = new DOMParser();
var doc = parser.parseFromString(html, 'text/html');
var threads = [];
var tbodies = doc.querySelectorAll('#threadlisttableid tbody[id^="normalthread_"]');
tbodies.forEach(function(tbody) {
var link = tbody.querySelector('th a[href*="thread-"]');
var title = tbody.querySelector('th a.xst') || tbody.querySelector('th .xst');
var titleText = title ? title.textContent : '';
var normalizedUrl = link && link.href ? normalizeThreadUrl(link.href) : '';
if (normalizedUrl) {
threads.push({
url: normalizedUrl,
title: titleText,
threadKey: getThreadKeyFromUrl(normalizedUrl)
});
}
});
return normalizeCachedThreads(threads);
}
function updateStatus(message, type) {
var status = document.getElementById('magnet-status');
if (status) {
status.textContent = message;
status.setAttribute('data-type', type || '');
// 默认样式
status.style.background = 'rgba(26,31,46,0.92)';
status.style.color = '#8892a4';
status.style.border = '1px solid rgba(255,255,255,0.06)';
if (type === 'loading') {
status.style.background = 'rgba(245,158,11,0.1)';
status.style.color = '#f59e0b';
status.style.borderColor = 'rgba(245,158,11,0.3)';
} else if (type === 'error') {
status.style.background = 'rgba(239,68,68,0.1)';
status.style.color = '#ef4444';
status.style.borderColor = 'rgba(239,68,68,0.3)';
} else if (type === 'done') {
status.style.background = 'rgba(16,185,129,0.1)';
status.style.color = '#10b981';
status.style.borderColor = 'rgba(16,185,129,0.3)';
}
if (type && type !== 'loading') {
scheduleStatePersist();
}
}
}
function updateCount(count) {
var countEl = document.getElementById('magnet-count-num');
if (countEl) countEl.textContent = count;
}
// === 进度条功能 ===
function updateProgress(current, total, label) {
var progressBar = document.getElementById('magnet-progress-bar');
var progressLabel = document.getElementById('magnet-progress-label');
var progressPercent = document.getElementById('magnet-progress-percent');
if (progressBar) {
var percent = total > 0 ? Math.round((current / total) * 100) : 0;
progressBar.style.width = percent + '%';
}
if (progressLabel) {
progressLabel.textContent = label || ('进度: ' + current + '/' + total);
}
if (progressPercent) {
var percent = total > 0 ? Math.round((current / total) * 100) : 0;
progressPercent.textContent = percent + '%';
}
}
function resetProgress() {
updateProgress(0, 0, '等待开始');
}
// === 收藏夹功能 ===
var FAVORITES_KEY = 'magnet-favorites';
var favoritesCache = null;
function loadFavorites() {
if (favoritesCache !== null) {
return favoritesCache;
}
try {
var stored = localStorage.getItem(FAVORITES_KEY);
favoritesCache = stored ? JSON.parse(stored) : [];
} catch (e) {
favoritesCache = [];
}
return favoritesCache;
}
function saveFavorites(favorites) {
try {
localStorage.setItem(FAVORITES_KEY, JSON.stringify(favorites));
favoritesCache = favorites;
} catch (e) {
log('保存收藏失败: ' + e);
}
}
function isFavorited(link) {
var favorites = loadFavorites();
return favorites.some(function(f) { return f.link === link; });
}
function toggleFavorite(title, link, btn) {
var favorites = loadFavorites();
var existingIndex = favorites.findIndex(function(f) { return f.link === link; });
if (existingIndex >= 0) {
favorites.splice(existingIndex, 1);
btn.classList.remove('is-favorite');
btn.innerHTML = '♡';
btn.title = '收藏';
} else {
favorites.push({
title: title,
link: link,
addedAt: Date.now()
});
btn.classList.add('is-favorite');
btn.innerHTML = '♥';
btn.title = '取消收藏';
}
saveFavorites(favorites);
}
function renderFavoritesList() {
var list = document.getElementById('magnet-favorites-list');
if (!list) return;
var favorites = loadFavorites();
list.innerHTML = '';
if (favorites.length === 0) {
list.innerHTML = '
📭
暂无收藏\br>点击结果列表中的 ♡ 按钮添加收藏
';
return;
}
favorites.forEach(function(fav) {
var item = document.createElement('div');
item.className = 'magnet-favorite-item';
var titleEl = document.createElement('div');
titleEl.className = 'magnet-favorite-title';
titleEl.textContent = fav.title;
titleEl.title = fav.title;
var actionsEl = document.createElement('div');
actionsEl.className = 'magnet-favorite-actions';
var copyBtn = document.createElement('button');
copyBtn.className = 'magnet-favorite-copy';
copyBtn.textContent = '复制';
copyBtn.onclick = function() {
navigator.clipboard.writeText(fav.link)
.then(function() {
copyBtn.textContent = '已复制';
setTimeout(function() { copyBtn.textContent = '复制'; }, 1000);
});
};
var removeBtn = document.createElement('button');
removeBtn.className = 'magnet-favorite-remove';
removeBtn.textContent = '删除';
removeBtn.onclick = function() {
var favorites = loadFavorites();
var idx = favorites.findIndex(function(f) { return f.link === fav.link; });
if (idx >= 0) {
favorites.splice(idx, 1);
saveFavorites(favorites);
renderFavoritesList();
}
};
actionsEl.appendChild(copyBtn);
actionsEl.appendChild(removeBtn);
item.appendChild(titleEl);
item.appendChild(actionsEl);
list.appendChild(item);
});
}
// === 历史记录功能 ===
var HISTORY_KEY = 'magnet-search-history';
var MAX_HISTORY = 20;
function loadSearchHistory() {
try {
var stored = localStorage.getItem(HISTORY_KEY);
return stored ? JSON.parse(stored) : [];
} catch (e) {
return [];
}
}
function saveSearchHistory(keyword) {
if (!keyword || typeof keyword !== 'string' || !keyword.trim()) {
return;
}
keyword = keyword.trim();
var history = loadSearchHistory();
// 移除已存在的相同关键词
var idx = history.indexOf(keyword);
if (idx >= 0) {
history.splice(idx, 1);
}
// 添加到开头
history.unshift(keyword);
// 限制数量
if (history.length > MAX_HISTORY) {
history = history.slice(0, MAX_HISTORY);
}
try {
localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
} catch (e) {
log('保存历史记录失败: ' + e);
}
}
function showHistoryDropdown(input) {
var history = loadSearchHistory();
if (history.length === 0) {
return;
}
// 移除已存在的下拉框
var existing = document.querySelector('.magnet-history-dropdown');
if (existing) existing.remove();
var dropdown = document.createElement('div');
dropdown.className = 'magnet-history-dropdown';
history.forEach(function(kw) {
var item = document.createElement('div');
item.className = 'magnet-history-item';
item.textContent = kw;
item.onclick = function() {
input.value = kw;
dropdown.remove();
input.focus();
};
dropdown.appendChild(item);
});
var clearItem = document.createElement('div');
clearItem.className = 'magnet-history-clear';
clearItem.textContent = '清空历史';
clearItem.onclick = function() {
localStorage.removeItem(HISTORY_KEY);
dropdown.remove();
};
dropdown.appendChild(clearItem);
input.parentNode.style.position = 'relative';
input.parentNode.appendChild(dropdown);
// 点击外部关闭
setTimeout(function() {
document.addEventListener('click', function closeDropdown(e) {
if (!dropdown.contains(e.target)) {
dropdown.remove();
document.removeEventListener('click', closeDropdown);
}
});
}, 100);
}
// === 通知功能 ===
function playNotificationSound() {
try {
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = audioContext.createOscillator();
var gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800;
oscillator.type = 'sine';
gainNode.gain.setValue(0.3);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.3);
} catch (e) {
log('播放提示音失败: ' + e);
}
}
function showBrowserNotification(title, body) {
if (!('Notification' in window)) {
return;
}
if (Notification.permission === 'granted') {
new Notification(title, { body: body, icon: chrome.runtime ? chrome.runtime.getURL('icon.png') : undefined });
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(function(permission) {
if (permission === 'granted') {
new Notification(title, { body: body, icon: chrome.runtime ? chrome.runtime.getURL('icon.png') : undefined });
}
});
}
}
function notifyComplete(count, duration) {
playNotificationSound();
var durationText = '';
if (duration && duration > 0) {
var seconds = Math.floor(duration / 1000);
if (seconds >= 60) {
durationText = ',耗时 ' + Math.floor(seconds / 60) + ' 分 ' + (seconds % 60) + ' 秒';
} else {
durationText = ',耗时 ' + seconds + ' 秒';
}
}
showBrowserNotification('磁力链接抓取完成', '共获取 ' + count + ' 个磁力链接' + durationText);
}
var countEl = document.getElementById('magnet-count-num');
if (countEl) countEl.textContent = count;
}
function clearMagnetList(skipPersist) {
var list = document.getElementById('magnet-list');
if (list) list.innerHTML = '';
allMagnetLinks = [];
allMagnetRecords = [];
magnetRecordMap = Object.create(null);
updateCount(0);
if (!skipPersist) {
scheduleStatePersist();
}
}
function addMagnetItem(title, link, options) {
var list = document.getElementById('magnet-list');
if (!list) return;
var safeTitle = typeof title === 'string' ? title : String(title || '');
var safeLink = typeof link === 'string' ? link : String(link || '');
if (!safeLink) return;
if (magnetRecordMap[safeLink]) {
return;
}
magnetRecordMap[safeLink] = safeTitle || '恢复记录';
allMagnetRecords.push({
title: magnetRecordMap[safeLink],
link: safeLink
});
allMagnetLinks.push(safeLink);
var item = document.createElement('div');
item.className = 'magnet-item';
var titleEl = document.createElement('span');
titleEl.className = 'magnet-title';
titleEl.title = safeTitle;
titleEl.textContent = safeTitle;
var btnContainer = document.createElement('div');
btnContainer.style.cssText = 'display:flex;gap:6px;flex-shrink:0;';
var copyBtn = document.createElement('button');
copyBtn.className = 'magnet-copy-btn';
copyBtn.setAttribute('data-magnet', safeLink);
copyBtn.textContent = '复制';
// 收藏按钮
var favoriteBtn = document.createElement('button');
favoriteBtn.className = 'magnet-favorite-btn';
favoriteBtn.innerHTML = '♡';
favoriteBtn.title = '收藏';
favoriteBtn.onclick = function() {
toggleFavorite(safeTitle, safeLink, favoriteBtn);
};
// 检查是否已收藏
isFavorite(safeLink).then(function(isFav) {
if (isFav) {
favoriteBtn.classList.add('is-favorite');
favoriteBtn.title = '取消收藏';
}
});
titleEl.onclick = function() {
navigator.clipboard.writeText(safeLink)
.then(function() {
titleEl.textContent = '已复制: ' + safeTitle.substring(0, 20) + '...';
setTimeout(function() {
titleEl.textContent = safeTitle;
}, 1500);
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
log('标题复制失败: ' + errorMsg);
updateStatus('复制失败,请检查剪贴板权限', 'error');
});
};
copyBtn.onclick = function() {
navigator.clipboard.writeText(safeLink)
.then(function() {
copyBtn.textContent = '已复制';
setTimeout(function() {
copyBtn.textContent = '复制';
}, 1000);
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
log('按钮复制失败: ' + errorMsg);
updateStatus('复制失败,请检查剪贴板权限', 'error');
});
};
item.appendChild(titleEl);
btnContainer.appendChild(favoriteBtn);
btnContainer.appendChild(copyBtn);
item.appendChild(btnContainer);
list.appendChild(item);
setPanelView('results');
updateCount(list.children.length);
if (!options || !options.skipPersist) {
scheduleStatePersist();
}
}
var list = document.getElementById('magnet-list');
if (!list) return;
var safeTitle = typeof title === 'string' ? title : String(title || '');
var safeLink = typeof link === 'string' ? link : String(link || '');
if (!safeLink) return;
if (magnetRecordMap[safeLink]) {
return;
}
magnetRecordMap[safeLink] = safeTitle || '恢复记录';
allMagnetRecords.push({
title: magnetRecordMap[safeLink],
link: safeLink
});
allMagnetLinks.push(safeLink);
var item = document.createElement('div');
item.className = 'magnet-item';
var titleEl = document.createElement('span');
titleEl.className = 'magnet-title';
titleEl.title = safeTitle;
titleEl.textContent = safeTitle;
var copyBtn = document.createElement('button');
copyBtn.className = 'magnet-copy-btn';
copyBtn.setAttribute('data-magnet', safeLink);
copyBtn.textContent = '复制';
titleEl.onclick = function() {
navigator.clipboard.writeText(safeLink)
.then(function() {
titleEl.textContent = '已复制: ' + safeTitle.substring(0, 20) + '...';
setTimeout(function() {
titleEl.textContent = safeTitle;
}, 1500);
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
log('标题复制失败: ' + errorMsg);
updateStatus('复制失败,请检查剪贴板权限', 'error');
});
};
copyBtn.onclick = function() {
navigator.clipboard.writeText(safeLink)
.then(function() {
copyBtn.textContent = '已复制';
setTimeout(function() {
copyBtn.textContent = '复制';
}, 1000);
})
.catch(function(err) {
var errorMsg = err && err.message ? err.message : '复制失败';
log('按钮复制失败: ' + errorMsg);
updateStatus('复制失败,请检查剪贴板权限', 'error');
});
};
item.appendChild(titleEl);
item.appendChild(copyBtn);
list.appendChild(item);
setPanelView('results');
updateCount(list.children.length);
if (!options || !options.skipPersist) {
scheduleStatePersist();
}
}
function extractMagnets() {
var magnetPattern = /magnet:\?xt=urn:btih:[a-fA-F0-9]{32,40}/gi;
var links = new Set();
function walk(node) {
if (node.nodeType === Node.TEXT_NODE) {
var matches = node.textContent.match(magnetPattern);
if (matches) matches.forEach(function(m) { links.add(m); });
} else if (node.nodeType === Node.ELEMENT_NODE) {
var tag = node.tagName.toLowerCase();
if (tag !== 'script' && tag !== 'style' && tag !== 'noscript') {
node.childNodes.forEach(walk);
}
}
}
walk(document.body);
document.querySelectorAll('a[href^="magnet:"]').forEach(function(a) { links.add(a.href); });
return Array.from(links);
}
var speedMode = 'fast';
var speedConfig = {
slow: {
thread: [300, 600],
page: [500, 900],
concurrency: 1,
fetchTimeout: 25000,
messageTimeout: 30000
},
medium: {
thread: [80, 180],
page: [150, 300],
concurrency: 2,
fetchTimeout: 18000,
messageTimeout: 22000
},
fast: {
thread: [0, 60],
page: [60, 150],
concurrency: 4,
fetchTimeout: 14000,
messageTimeout: 18000
},
ultrafast: {
thread: [0, 20],
page: [20, 80],
concurrency: 6,
fetchTimeout: 10000,
messageTimeout: 14000
}
};
function getSpeedProfile() {
return speedConfig[speedMode] || speedConfig.fast;
}
function getSpeedDelay(type) {
var profile = getSpeedProfile();
var range = profile[type] || [0, 0];
return Math.random() * (range[1] - range[0]) + range[0];
}
function getThreadConcurrency() {
return Math.max(1, getSpeedProfile().concurrency || 1);
}
function getFetchTimeout() {
return getSpeedProfile().fetchTimeout || 15000;
}
function getMessageTimeout() {
return getSpeedProfile().messageTimeout || 20000;
}
function sleep(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }); }
function sendRuntimeMessage(payload, timeoutMs) {
return new Promise(function(resolve, reject) {
if (!chrome.runtime || !chrome.runtime.id) {
reject(new Error('扩展已失效,请刷新页面'));
return;
}
var finished = false;
var safeTimeoutMs = Number(timeoutMs);
if (!Number.isFinite(safeTimeoutMs) || safeTimeoutMs <= 0) {
safeTimeoutMs = 15000;
}
var timer = setTimeout(function() {
if (finished) return;
finished = true;
reject(new Error('请求超时,请稍后重试'));
}, safeTimeoutMs);
try {
chrome.runtime.sendMessage(payload, function(response) {
if (finished) return;
finished = true;
clearTimeout(timer);
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
return;
}
resolve(response);
});
} catch (err) {
if (finished) return;
finished = true;
clearTimeout(timer);
reject(err);
}
});
}
function buildKeywordList(keyword) {
return String(keyword || '')
.split(',')
.map(function(item) { return item.trim(); })
.filter(function(item) { return !!item; });
}
function filterThreadsByKeywords(threadList, keywords) {
var normalizedThreads = normalizeCachedThreads(threadList);
if (!keywords || keywords.length === 0) {
return normalizedThreads;
}
return normalizedThreads.filter(function(thread) {
return keywords.some(function(keyword) {
return thread.title.indexOf(keyword) !== -1;
});
});
}
function mergeCoverageThreads(targetMap, threads) {
normalizeCachedThreads(threads).forEach(function(thread) {
if (!targetMap[thread.threadKey]) {
targetMap[thread.threadKey] = thread;
return;
}
if (!targetMap[thread.threadKey].title && thread.title) {
targetMap[thread.threadKey].title = thread.title;
}
if (!targetMap[thread.threadKey].url && thread.url) {
targetMap[thread.threadKey].url = thread.url;
}
});
}
function coverageMapToList(targetMap) {
return Object.keys(targetMap).map(function(threadKey) {
return targetMap[threadKey];
});
}
function getSmartFrontRefreshPages(startPage, endPage) {
if (startPage !== 1) {
return 0;
}
return Math.min(20, Math.max(0, endPage - startPage + 1));
}
async function getCachedCoveragePlan(forumKey, startPage, endPage, frontRefreshPages) {
try {
var response = await sendRuntimeMessage({
action: 'cacheGetCoveragePlan',
forumKey: forumKey,
startPage: startPage,
endPage: endPage,
frontRefreshPages: frontRefreshPages
}, 8000);
if (!response || !response.ok) {
log('读取缓存计划失败: ' + (response && response.error ? response.error : '空响应'));
return null;
}
return response;
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('读取缓存计划异常: ' + errorMsg);
return null;
}
}
async function saveCoverageSnapshot(forumKey, startPage, endPage, threads, frontRefreshPages, strategy) {
var normalizedThreads = normalizeCachedThreads(threads);
if (!forumKey || normalizedThreads.length === 0) {
return;
}
try {
var response = await sendRuntimeMessage({
action: 'cacheSaveCoverage',
forumKey: forumKey,
startPage: startPage,
endPage: endPage,
frontRefreshPages: frontRefreshPages,
strategy: strategy,
crawledAt: Date.now(),
threads: normalizedThreads
}, 12000);
if (!response || !response.ok) {
log('保存范围缓存失败: ' + (response && response.error ? response.error : '空响应'));
}
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('保存范围缓存异常: ' + errorMsg);
}
}
async function savePageCoverageSnapshot(forumKey, page, threads) {
var normalizedThreads = normalizeCachedThreads(threads);
try {
var response = await sendRuntimeMessage({
action: 'cacheSavePageCoverage',
forumKey: forumKey,
page: page,
crawledAt: Date.now(),
threads: normalizedThreads
}, 12000);
if (!response || !response.ok) {
log('保存页缓存失败: ' + (response && response.error ? response.error : '空响应'));
}
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('保存页缓存异常: ' + errorMsg);
}
}
async function getCachedThreadMagnets(forumKey, threads) {
var normalizedThreads = normalizeCachedThreads(threads);
if (!forumKey || normalizedThreads.length === 0) {
return [];
}
try {
var response = await sendRuntimeMessage({
action: 'cacheGetThreadMagnets',
forumKey: forumKey,
threads: normalizedThreads
}, 12000);
if (!response || !response.ok || !Array.isArray(response.threads)) {
log('读取帖子磁链缓存失败: ' + (response && response.error ? response.error : '空响应'));
return [];
}
return response.threads.map(function(thread) {
return {
threadKey: typeof thread.threadKey === 'string' ? thread.threadKey : getThreadKeyFromUrl(thread.url),
url: normalizeThreadUrl(thread.url),
title: typeof thread.title === 'string' ? thread.title : '',
magnets: normalizeMagnetList(thread.magnets)
};
});
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('读取帖子磁链缓存异常: ' + errorMsg);
return [];
}
}
async function saveThreadMagnetsToCache(forumKey, threads) {
var normalizedThreads = [];
(Array.isArray(threads) ? threads : []).forEach(function(thread) {
if (!thread || typeof thread !== 'object') {
return;
}
var normalizedUrl = normalizeThreadUrl(thread.url);
var threadKey = typeof thread.threadKey === 'string' && thread.threadKey
? thread.threadKey
: getThreadKeyFromUrl(normalizedUrl);
var magnets = normalizeMagnetList(thread.magnets);
if (!normalizedUrl || !threadKey || magnets.length === 0) {
return;
}
normalizedThreads.push({
threadKey: threadKey,
url: normalizedUrl,
title: typeof thread.title === 'string' ? thread.title : '',
magnets: magnets
});
});
if (!forumKey || normalizedThreads.length === 0) {
return;
}
try {
var response = await sendRuntimeMessage({
action: 'cacheSaveThreadMagnets',
forumKey: forumKey,
syncedAt: Date.now(),
threads: normalizedThreads
}, 12000);
if (!response || !response.ok) {
log('保存帖子磁链缓存失败: ' + (response && response.error ? response.error : '空响应'));
}
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('保存帖子磁链缓存异常: ' + errorMsg);
}
}
function formatTimeLabel(timestamp) {
var value = Number(timestamp);
if (!value) {
return '无';
}
var date = new Date(value);
var month = String(date.getMonth() + 1).padStart(2, '0');
var day = String(date.getDate()).padStart(2, '0');
var hour = String(date.getHours()).padStart(2, '0');
var minute = String(date.getMinutes()).padStart(2, '0');
return month + '-' + day + ' ' + hour + ':' + minute;
}
function formatBytesLabel(bytes) {
var size = Number(bytes);
if (!Number.isFinite(size) || size <= 0) {
return '0 B';
}
if (size < 1024) {
return size + ' B';
}
if (size < 1024 * 1024) {
return (size / 1024).toFixed(1) + ' KB';
}
return (size / (1024 * 1024)).toFixed(2) + ' MB';
}
async function getCacheOverview(forumKey) {
try {
var response = await sendRuntimeMessage({
action: 'cacheGetOverview',
forumKey: forumKey,
limit: 12
}, 12000);
if (!response || !response.ok) {
log('读取缓存总览失败: ' + (response && response.error ? response.error : '空响应'));
return null;
}
return response;
} catch (e) {
var errorMsg = e && e.message ? e.message : String(e);
log('读取缓存总览异常: ' + errorMsg);
return null;
}
}
function renderCacheOverview(cacheOverview) {
var panel = document.getElementById('magnet-cache-panel');
if (!panel) {
return;
}
if (!cacheOverview || !cacheOverview.summary) {
panel.innerHTML = '
缓存状态
暂无缓存数据
';
return;
}
var summary = cacheOverview.summary;
var recentThreads = Array.isArray(cacheOverview.recentThreads) ? cacheOverview.recentThreads : [];
var recentCoverages = Array.isArray(cacheOverview.recentCoverages) ? cacheOverview.recentCoverages : [];
var html = '';
html += '