diff --git a/content.js b/content.js
index 7971d5f..37140d6 100644
--- a/content.js
+++ b/content.js
@@ -472,12 +472,100 @@
'}',
'.magnet-empty-state-text{',
' font-size:14px;line-height:1.6;',
+ '}',
+
+ /* === 进度条 === */',
+ '.magnet-progress-container{',
+ ' width:100%;height:6px;background:var(--m-bg-secondary);border-radius:3px;overflow:hidden;margin-top:8px;',
+ '}',
+ '.magnet-progress-bar{',
+ ' height:100%;background:linear-gradient(90deg, var(--m-accent), #00f5c4);border-radius:3px;transition:width 0.3s ease;',
+ '}',
+ '.magnet-progress-text{',
+ ' display:flex;justify-content:space-between;font-size:11px;color:var(--m-text-muted);margin-top:4px;',
+ '}',
+
+ /* === 收藏按钮 === */',
+ '.magnet-favorite-btn{',
+ ' padding:6px 10px;background:transparent;border:1px solid var(--m-border);border-radius:8px;cursor:pointer;font-size:11px;color:var(--m-text-muted);transition:all 0.2s ease;',
+ '}',
+ '.magnet-favorite-btn:hover{',
+ ' border-color:var(--m-accent-secondary);color:var(--m-accent-secondary);',
+ '}',
+ '.magnet-favorite-btn.is-favorite{',
+ ' background:rgba(167,139,250,0.15);border-color:var(--m-accent-secondary);color:var(--m-accent-secondary);',
+ '}',
+
+ /* === 收藏视图 === */',
+ '#magnet-favorites-view .magnet-favorite-item{',
+ ' display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--m-bg-card);border:1px solid var(--m-border);border-radius:var(--m-radius-md);margin-bottom:8px;',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-item:hover{',
+ ' border-color:var(--m-border-accent);',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-title{',
+ ' flex:1;font-size:12px;color:var(--m-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-actions{',
+ ' display:flex;gap:6px;',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-copy,',
+ '#magnet-favorites-view .magnet-favorite-remove{',
+ ' padding:5px 8px;border:none;border-radius:6px;cursor:pointer;font-size:10px;font-weight:600;transition:all 0.2s ease;',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-copy{',
+ ' background:linear-gradient(135deg, var(--m-accent), #00f5c4);color:var(--m-bg-deep);',
+ '}',
+ '#magnet-favorites-view .magnet-favorite-remove{',
+ ' background:rgba(239,68,68,0.15);color:var(--m-error);',
+ '}',
+
+ /* === 历史记录下拉 === */',
+ '.magnet-history-dropdown{',
+ ' position:absolute;top:100%;left:0;right:0;background:var(--m-bg-card);border:1px solid var(--m-border);border-radius:var(--m-radius-md);box-shadow:var(--m-shadow-lg);z-index:100;max-height:200px;overflow-y:auto;',
+ '}',
+ '.magnet-history-item{',
+ ' padding:10px 14px;cursor:pointer;font-size:12px;color:var(--m-text-primary);transition:background 0.2s ease;',
+ '}',
+ '.magnet-history-item:hover{',
+ ' background:var(--m-bg-secondary);',
+ '}',
+ '.magnet-history-item:first-child{',
+ ' border-radius:var(--m-radius-md) var(--m-radius-md) 0 0;',
+ '}',
+ '.magnet-history-item:last-child{',
+ ' border-radius:0 0 var(--m-radius-md) var(--m-radius-md);',
+ '}',
+ '.magnet-history-clear{',
+ ' padding:10px 14px;border-top:1px solid var(--m-border);font-size:11px;color:var(--m-error);cursor:pointer;text-align:center;',
'}'
].join('');
document.head.appendChild(style);
}
function setPanelView(viewName) {
+ var resultsView = document.getElementById('magnet-results-view');
+ var cacheView = document.getElementById('magnet-cache-view');
+ var favoritesView = document.getElementById('magnet-favorites-view');
+ var resultsBtn = document.getElementById('magnet-view-results');
+ var cacheBtn = document.getElementById('magnet-view-cache');
+ var favoritesBtn = document.getElementById('magnet-view-favorites');
+
+ if (!resultsView || !cacheView || !favoritesView || !resultsBtn || !cacheBtn || !favoritesBtn) {
+ return;
+ }
+
+ resultsView.classList.toggle('is-active', viewName === 'results');
+ cacheView.classList.toggle('is-active', viewName === 'cache');
+ favoritesView.classList.toggle('is-active', viewName === 'favorites');
+ resultsBtn.classList.toggle('is-active', viewName === 'results');
+ cacheBtn.classList.toggle('is-active', viewName === 'cache');
+ favoritesBtn.classList.toggle('is-active', viewName === 'favorites');
+
+ if (viewName === 'favorites') {
+ renderFavoritesList();
+ }
+ }
var resultsView = document.getElementById('magnet-results-view');
var cacheView = document.getElementById('magnet-cache-view');
var resultsBtn = document.getElementById('magnet-view-results');
@@ -527,7 +615,7 @@
var panel = document.createElement('div');
panel.id = 'magnet-floating-panel';
- panel.innerHTML = '
';
+ panel.innerHTML = '';
document.body.appendChild(panel);
setPanelView('results');
@@ -569,7 +657,48 @@
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() {
@@ -754,6 +883,280 @@
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 = '';
@@ -794,6 +1197,100 @@
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);
@@ -1796,6 +2293,11 @@
}
updateStatus('第' + page + '/' + context.normalizedEnd + '页...', 'loading');
+
+ // 更新进度条
+ var totalPages = context.normalizedEnd - (context.startPage || startPage) + 1;
+ var currentPage = page - (context.startPage || startPage) + 1;
+ updateProgress(currentPage, totalPages, '第' + page + '/' + context.normalizedEnd + '页');
var pageUrl = context.baseUrl + page + '.html';
try {
@@ -1876,6 +2378,18 @@
progressRuntime.startPage = earlyStart;
progressRuntime.endPage = earlyEnd;
progressRuntime.resumeFromPage = earlyStart;
+
+ // 重置进度条
+ resetProgress();
+
+ // 记录开始时间
+ var startTime = Date.now();
+ stopFetching = false;
+ progressRuntime.isRunning = true;
+ progressRuntime.stoppedByUser = false;
+ progressRuntime.startPage = earlyStart;
+ progressRuntime.endPage = earlyEnd;
+ progressRuntime.resumeFromPage = earlyStart;
var panel = createFloatingPanel();
var ball = document.getElementById('magnet-float-ball');
@@ -2010,6 +2524,28 @@
var keywordMsg = keyword ? ' (关键词:' + keyword + ' 匹配:' + searchContext.matchedThreads + '帖)' : '';
var failedMsg = searchContext.failedPages > 0 ? ',失败页:' + searchContext.failedPages : '';
+
+ // 更新进度条为100%
+ updateProgress(normalizedEnd - normalizedStart + 1, normalizedEnd - normalizedStart + 1, '已完成');
+
+ if (stopFetching) {
+ progressRuntime.stoppedByUser = true;
+ updateStatus('已停止 - 找到' + searchContext.allMagnets.size + '个磁力' + keywordMsg + ',已处理帖子:' + searchContext.totalFetched + failedMsg, 'error');
+ } else {
+ progressRuntime.stoppedByUser = false;
+ progressRuntime.resumeFromPage = normalizedEnd + 1;
+ updateStatus('完成! 共' + searchContext.allMagnets.size + '个磁力' + keywordMsg + ',已处理帖子:' + searchContext.totalFetched + failedMsg, 'done');
+
+ // 发送完成通知
+ var duration = Date.now() - startTime;
+ notifyComplete(searchContext.allMagnets.size, duration);
+
+ // 保存搜索历史
+ if (keyword) {
+ saveSearchHistory(keyword);
+ }
+ }
+ var failedMsg = searchContext.failedPages > 0 ? ',失败页:' + searchContext.failedPages : '';
if (stopFetching) {
progressRuntime.stoppedByUser = true;
updateStatus('已停止 - 找到' + searchContext.allMagnets.size + '个磁力' + keywordMsg + ',已处理帖子:' + searchContext.totalFetched + failedMsg, 'error');
@@ -2058,6 +2594,14 @@
var keywordDiv = document.createElement('div');
keywordDiv.className = 'magnet-control-row';
keywordDiv.innerHTML = '';
+
+ // 添加历史记录下拉功能
+ var keywordInput = keywordDiv.querySelector('#keyword-input');
+ if (keywordInput) {
+ keywordInput.addEventListener('focus', function() {
+ showHistoryDropdown(keywordInput);
+ });
+ }
var pageRange = document.createElement('div');
pageRange.className = 'magnet-control-row';
diff --git a/manifest.json b/manifest.json
index 026964c..cc52dfa 100644
--- a/manifest.json
+++ b/manifest.json
@@ -2,12 +2,13 @@
"manifest_version": 3,
"name": "涩花塘磁力助手",
"version": "1.3",
- "description": "一键获取磁力链接 - 暗色科技风UI",
+ "description": "一键获取磁力链接 - 暗色科技风UI + 收藏夹 + 吜索历史 + 宔完成通知",
"permissions": [
"activeTab",
"clipboardWrite",
"storage",
- "unlimitedStorage"
+ "unlimitedStorage",
+ "notifications"
],
"host_permissions": [
"http://sehuatang.net/*",