feat: 新增进度条、收藏夹、历史记录、 通知提醒功能
## 新功能 1. 进度条 - 可视化抓取进度 2. 收藏夹 - 持久收藏磁力链接(♡按钮) 3. 历史记录 - 关键词搜索历史(点击输入框显示) 4. 通知提醒 - 完成时提示音 + 浏览器通知 ## 技术实现 - 使用 localStorage 存储收藏和历史 - 使用 Web Audio API 播放提示音 - 使用 Notification API 显示浏览器通知 - CSS 进度条动画效果
This commit is contained in:
546
content.js
546
content.js
@@ -472,12 +472,100 @@
|
|||||||
'}',
|
'}',
|
||||||
'.magnet-empty-state-text{',
|
'.magnet-empty-state-text{',
|
||||||
' font-size:14px;line-height:1.6;',
|
' 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('');
|
].join('');
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPanelView(viewName) {
|
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 resultsView = document.getElementById('magnet-results-view');
|
||||||
var cacheView = document.getElementById('magnet-cache-view');
|
var cacheView = document.getElementById('magnet-cache-view');
|
||||||
var resultsBtn = document.getElementById('magnet-view-results');
|
var resultsBtn = document.getElementById('magnet-view-results');
|
||||||
@@ -527,7 +615,7 @@
|
|||||||
|
|
||||||
var panel = document.createElement('div');
|
var panel = document.createElement('div');
|
||||||
panel.id = 'magnet-floating-panel';
|
panel.id = 'magnet-floating-panel';
|
||||||
panel.innerHTML = '<div class="magnet-panel-header"><div class="magnet-panel-brand"><div class="magnet-panel-title">MAGNET LINKS</div><div class="magnet-panel-subtitle">智能抓取 · 缓存加速 · 一键复制</div></div><div class="magnet-panel-head-actions"><button id="magnet-view-results" class="magnet-panel-switch is-active">结果</button><button id="magnet-view-cache" class="magnet-panel-switch">缓存</button><button class="magnet-panel-close" title="关闭">×</button></div></div><div class="magnet-settings" id="magnet-settings"></div><div class="magnet-panel-content"><div id="magnet-results-view" class="magnet-view is-active"><div class="magnet-view-toolbar"><div><div class="magnet-view-title">搜索结果</div><div class="magnet-view-meta">关键词命中的磁力链接</div></div><div class="magnet-view-meta">共 <span id="magnet-count-num">0</span> 条</div></div><div class="magnet-list" id="magnet-list"></div></div><div id="magnet-cache-view" class="magnet-view"><div class="magnet-view-toolbar"><div><div class="magnet-view-title">缓存总览</div><div class="magnet-view-meta">数据统计与快照管理</div></div><div class="magnet-view-toolbar-actions"><button id="magnet-refresh-cache" class="magnet-panel-switch is-active">刷新</button><button id="magnet-clear-cache-inline" class="magnet-panel-switch">清空</button></div></div><div id="magnet-cache-panel"></div></div></div><div class="magnet-panel-footer"><div id="magnet-status">设置参数后开始抓取</div><button id="magnet-copy-all">一键复制全部</button></div>';
|
panel.innerHTML = '<div class="magnet-panel-header"><div class="magnet-panel-brand"><div class="magnet-panel-title">MAGNET LINKS</div><div class="magnet-panel-subtitle">智能抓取 · 缓存加速 · 一键复制</div></div><div class="magnet-panel-head-actions"><button id="magnet-view-results" class="magnet-panel-switch is-active">结果</button><button id="magnet-view-favorites" class="magnet-panel-switch">收藏</button><button id="magnet-view-cache" class="magnet-panel-switch">缓存</button><button class="magnet-panel-close" title="关闭">×</button></div></div><div class="magnet-settings" id="magnet-settings"></div><div class="magnet-panel-content"><div id="magnet-results-view" class="magnet-view is-active"><div class="magnet-view-toolbar"><div><div class="magnet-view-title">搜索结果</div><div class="magnet-view-meta">关键词命中的磁力链接</div></div><div class="magnet-view-meta">共 <span id="magnet-count-num">0</span> 条</div></div><div class="magnet-list" id="magnet-list"></div></div><div id="magnet-favorites-view" class="magnet-view"><div class="magnet-view-toolbar"><div><div class="magnet-view-title">我的收藏</div><div class="magnet-view-meta">持久保存的磁力链接</div></div><div class="magnet-view-toolbar-actions"><button id="magnet-clear-favorites" class="magnet-panel-switch">清空收藏</button></div></div><div id="magnet-favorites-list"></div></div><div id="magnet-cache-view" class="magnet-view"><div class="magnet-view-toolbar"><div><div class="magnet-view-title">缓存总览</div><div class="magnet-view-meta">数据统计与快照管理</div></div><div class="magnet-view-toolbar-actions"><button id="magnet-refresh-cache" class="magnet-panel-switch is-active">刷新</button><button id="magnet-clear-cache-inline" class="magnet-panel-switch">清空</button></div></div><div id="magnet-cache-panel"></div></div></div><div class="magnet-panel-footer"><div id="magnet-status">设置参数后开始抓取</div><div class="magnet-progress-container"><div class="magnet-progress-bar" id="magnet-progress-bar" style="width:0%"></div></div><div class="magnet-progress-text"><span id="magnet-progress-label">等待开始</span><span id="magnet-progress-percent">0%</span></div><button id="magnet-copy-all">一键复制全部</button></div>';
|
||||||
document.body.appendChild(panel);
|
document.body.appendChild(panel);
|
||||||
|
|
||||||
setPanelView('results');
|
setPanelView('results');
|
||||||
@@ -569,7 +657,48 @@
|
|||||||
panel.style.display = 'none';
|
panel.style.display = 'none';
|
||||||
ball.style.display = 'flex';
|
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');
|
var copyAllBtn = panel.querySelector('#magnet-copy-all');
|
||||||
if (copyAllBtn) {
|
if (copyAllBtn) {
|
||||||
copyAllBtn.onclick = function() {
|
copyAllBtn.onclick = function() {
|
||||||
@@ -754,6 +883,280 @@
|
|||||||
if (countEl) countEl.textContent = count;
|
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 = '<div class="magnet-empty-state"><div class="magnet-empty-state-icon">📭</div><div class="magnet-empty-state-text">暂无收藏\br>点击结果列表中的 ♡ 按钮添加收藏</div></div>';
|
||||||
|
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) {
|
function clearMagnetList(skipPersist) {
|
||||||
var list = document.getElementById('magnet-list');
|
var list = document.getElementById('magnet-list');
|
||||||
if (list) list.innerHTML = '';
|
if (list) list.innerHTML = '';
|
||||||
@@ -794,6 +1197,100 @@
|
|||||||
titleEl.title = safeTitle;
|
titleEl.title = safeTitle;
|
||||||
titleEl.textContent = 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');
|
var copyBtn = document.createElement('button');
|
||||||
copyBtn.className = 'magnet-copy-btn';
|
copyBtn.className = 'magnet-copy-btn';
|
||||||
copyBtn.setAttribute('data-magnet', safeLink);
|
copyBtn.setAttribute('data-magnet', safeLink);
|
||||||
@@ -1796,6 +2293,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateStatus('第' + page + '/' + context.normalizedEnd + '页...', 'loading');
|
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';
|
var pageUrl = context.baseUrl + page + '.html';
|
||||||
try {
|
try {
|
||||||
@@ -1876,6 +2378,18 @@
|
|||||||
progressRuntime.startPage = earlyStart;
|
progressRuntime.startPage = earlyStart;
|
||||||
progressRuntime.endPage = earlyEnd;
|
progressRuntime.endPage = earlyEnd;
|
||||||
progressRuntime.resumeFromPage = earlyStart;
|
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 panel = createFloatingPanel();
|
||||||
var ball = document.getElementById('magnet-float-ball');
|
var ball = document.getElementById('magnet-float-ball');
|
||||||
@@ -2010,6 +2524,28 @@
|
|||||||
|
|
||||||
var keywordMsg = keyword ? ' (关键词:' + keyword + ' 匹配:' + searchContext.matchedThreads + '帖)' : '';
|
var keywordMsg = keyword ? ' (关键词:' + keyword + ' 匹配:' + searchContext.matchedThreads + '帖)' : '';
|
||||||
var failedMsg = searchContext.failedPages > 0 ? ',失败页:' + searchContext.failedPages : '';
|
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) {
|
if (stopFetching) {
|
||||||
progressRuntime.stoppedByUser = true;
|
progressRuntime.stoppedByUser = true;
|
||||||
updateStatus('已停止 - 找到' + searchContext.allMagnets.size + '个磁力' + keywordMsg + ',已处理帖子:' + searchContext.totalFetched + failedMsg, 'error');
|
updateStatus('已停止 - 找到' + searchContext.allMagnets.size + '个磁力' + keywordMsg + ',已处理帖子:' + searchContext.totalFetched + failedMsg, 'error');
|
||||||
@@ -2058,6 +2594,14 @@
|
|||||||
var keywordDiv = document.createElement('div');
|
var keywordDiv = document.createElement('div');
|
||||||
keywordDiv.className = 'magnet-control-row';
|
keywordDiv.className = 'magnet-control-row';
|
||||||
keywordDiv.innerHTML = '<input type="text" id="keyword-input" placeholder="关键词(逗号分隔多关键词)" style="width:100%;padding:11px 14px;border:1px solid rgba(0,212,170,0.3);border-radius:12px;font-size:13px;box-sizing:border-box;background:#0f1419;color:#f0f4f8;box-shadow:inset 0 1px 2px rgba(0,0,0,0.2)">';
|
keywordDiv.innerHTML = '<input type="text" id="keyword-input" placeholder="关键词(逗号分隔多关键词)" style="width:100%;padding:11px 14px;border:1px solid rgba(0,212,170,0.3);border-radius:12px;font-size:13px;box-sizing:border-box;background:#0f1419;color:#f0f4f8;box-shadow:inset 0 1px 2px rgba(0,0,0,0.2)">';
|
||||||
|
|
||||||
|
// 添加历史记录下拉功能
|
||||||
|
var keywordInput = keywordDiv.querySelector('#keyword-input');
|
||||||
|
if (keywordInput) {
|
||||||
|
keywordInput.addEventListener('focus', function() {
|
||||||
|
showHistoryDropdown(keywordInput);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var pageRange = document.createElement('div');
|
var pageRange = document.createElement('div');
|
||||||
pageRange.className = 'magnet-control-row';
|
pageRange.className = 'magnet-control-row';
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "涩花塘磁力助手",
|
"name": "涩花塘磁力助手",
|
||||||
"version": "1.3",
|
"version": "1.3",
|
||||||
"description": "一键获取磁力链接 - 暗色科技风UI",
|
"description": "一键获取磁力链接 - 暗色科技风UI + 收藏夹 + 吜索历史 + 宔完成通知",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"activeTab",
|
"activeTab",
|
||||||
"clipboardWrite",
|
"clipboardWrite",
|
||||||
"storage",
|
"storage",
|
||||||
"unlimitedStorage"
|
"unlimitedStorage",
|
||||||
|
"notifications"
|
||||||
],
|
],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
"http://sehuatang.net/*",
|
"http://sehuatang.net/*",
|
||||||
|
|||||||
Reference in New Issue
Block a user