// ==UserScript==
// @name 哔哩哔哩订阅管理 / 批量取消订阅合集
// @author 安和(AHCorn)
// @namespace https://github.com/AHCorn/Bilibili-Batch-Unsubscribe
// @version 1.0.3
// @license MIT
// @description 批量管理哔哩哔哩订阅,可实现一键取消所有订阅。
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @match https://space.bilibili.com/*/favlist*
// @run-at document-end
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
#bilibili-batch-unsubscribe-panel {
position: fixed;
top: 7%;
left: 13%;
right: 13%;
bottom: 7%;
z-index: 10000;
background: #fff;
padding: 40px;
overflow: auto;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
display: flex;
flex-direction: column;
transition: all 0.5s ease-in-out;
transform: scale(1);
opacity: 1;
}
#bilibili-batch-unsubscribe-panel .panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
font-size: 24px;
color: #333;
font-weight: bold;
}
#bilibili-batch-unsubscribe-panel .close-btn {
cursor: pointer;
font-size: 30px;
color: #999;
transition: color 0.3s;
}
#bilibili-batch-unsubscribe-panel .close-btn:hover {
color: #666;
}
#bilibili-batch-unsubscribe-panel .subscription-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(45%, 1fr));
gap: 20px;
overflow: auto;
padding-bottom: 20px;
}
#bilibili-batch-unsubscribe-panel .subscription-item {
display: flex;
align-items: center;
padding: 20px;
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 15px;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transform: translateZ(0);
}
#bilibili-batch-unsubscribe-panel .subscription-item:hover {
background-color: #f8f8f8;
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0,0,0,0.2);
}
#bilibili-batch-unsubscribe-panel .subscription-item input[type='checkbox'] {
flex: none;
margin-right: 15px;
appearance: none;
width: 24px;
height: 24px;
border: 2px solid #4CAF50;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
#bilibili-batch-unsubscribe-panel .subscription-item input[type='checkbox']:checked {
background-color: #4CAF50;
border-color: #4CAF50;
}
#bilibili-batch-unsubscribe-panel .subscription-item label {
flex-grow: 1;
font-size: 16px;
color: #333;
transition: color 0.3s;
}
#bilibili-batch-unsubscribe-panel .action-buttons {
display: flex;
justify-content: flex-end;
margin-top: auto;
padding-top: 20px;
}
#bilibili-batch-unsubscribe-panel .btn {
cursor: pointer;
background: linear-gradient(135deg, #6e8efb, #88c9f9);
color: #fff;
border: none;
padding: 12px 25px;
margin: 0 10px;
border-radius: 8px;
font-weight: 600;
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
#bilibili-batch-unsubscribe-panel .btn:hover {
transform: scale(1.05);
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
}
.hidden {
display: none !important;
}
#bilibili-batch-unsubscribe-panel input[type="text"] {
padding: 10px;
border: 2px solid #6e8efb;
border-radius: 5px;
margin-right: 10px;
font-size: 16px;
flex-grow: 1;
transition: border-color 0.3s, box-shadow 0.3s;
}
#bilibili-batch-unsubscribe-panel input[type="text"]:focus {
border-color: #4CAF50;
box-shadow: 0 0 5px rgba(0, 128, 0, 0.3);
}
#bilibili-batch-unsubscribe-panel .action-buttons {
display: flex;
justify-content: space-between;
margin-top: auto;
padding-top: 20px;
}
`);
const panelHTML = `
`;
const panel = document.createElement('div');
panel.id = 'bilibili-batch-unsubscribe-panel';
panel.className = 'hidden';
panel.innerHTML = panelHTML;
document.body.appendChild(panel);
const subscriptionList = panel.querySelector('.subscription-list');
const loadingMessage = panel.querySelector('.loading-message');
async function confirmAndUnsubscribe(title) {
const subscriptionItems = Array.from(panel.querySelectorAll('.subscription-item'));
for (let item of subscriptionItems) {
const itemTitle = item.querySelector('label').textContent.trim();
if (itemTitle === title) {
const fid = item.querySelector('input[type="checkbox"]').value;
await simulateUnsubscribeByTitle(title, fid);
break;
}
}
}
async function simulateUnsubscribeByTitle(title, fid) {
return new Promise((resolve, reject) => {
console.log(`正在尝试取消订阅: ${title}`);
const favItem = document.querySelector(`li[fid="${fid}"] a[title="${title}"]`);
if (favItem) {
const unsubscribeButton = favItem.closest('li').querySelector('.be-dropdown-item');
if (unsubscribeButton) {
unsubscribeButton.click();
setTimeout(() => {
console.log(`取消订阅: ${title} 成功`);
resolve();
}, 1000);
} else {
console.error('未找到取消订阅按钮');
reject('未找到取消订阅按钮');
}
} else {
console.error('未找到对应的订阅项');
reject('未找到对应的订阅项');
}
});
}
function togglePanel() {
if (panel.classList.contains('hidden')) {
panel.classList.remove('hidden');
loadAndDisplaySubscriptions();
} else {
panel.classList.add('hidden');
}
}
function loadAndDisplaySubscriptions() {
const loadingMessage = document.createElement('div');
loadingMessage.className = 'loading-message';
loadingMessage.textContent = '正在加载订阅列表,请稍候...';
panel.appendChild(loadingMessage);
const containers = Array.from(document.querySelectorAll('.nav-container.fav-container'));
const targetContainer = containers.find(container => container.querySelector('p')?.textContent.includes('我的收藏和订阅'));
if (!targetContainer) {
console.error('指定容器未找到');
return;
}
const favListContainer = targetContainer.querySelector('.fav-list-container');
if (!favListContainer) {
console.error('未找到订阅列表');
return;
}
let lastHeight = favListContainer.scrollHeight;
let attempts = 0;
const checkScroll = () => {
const currentHeight = favListContainer.scrollHeight;
favListContainer.scrollTop += favListContainer.clientHeight / 2;
if (lastHeight !== currentHeight) {
lastHeight = currentHeight;
attempts = 0;
setTimeout(checkScroll, 10);
} else if (attempts < 50) {
attempts++;
setTimeout(checkScroll, 20);
} else {
console.log('没有更多内容');
displayLoadedSubscriptions(targetContainer);
panel.removeChild(loadingMessage);
}
};
checkScroll();
}
function displayLoadedSubscriptions(container) {
const items = container.querySelectorAll('.fav-item');
const subscriptionList = document.querySelector('.subscription-list');
subscriptionList.innerHTML = '';
items.forEach((item) => {
const title = item.querySelector('.text').title;
const link = item.querySelector('.text').href;
const fid = item.getAttribute('fid');
const listItem = document.createElement('div');
listItem.classList.add('subscription-item');
listItem.innerHTML = `
查看`;
subscriptionList.appendChild(listItem);
});
}
// 关闭面板
const closeBtn = panel.querySelector('.close-btn');
closeBtn.addEventListener('click', togglePanel);
// 全选(修复筛选错误问题)
const selectAllBtn = panel.querySelector('#select-all');
selectAllBtn.addEventListener('click', () => {
const visibleCheckboxes = panel.querySelectorAll('.subscription-item:not([style*="display: none"]) input[type="checkbox"]');
visibleCheckboxes.forEach((checkbox) => {
checkbox.checked = true;
});
});
// 取消全选
const deselectAllBtn = panel.querySelector('#deselect-all');
deselectAllBtn.addEventListener('click', () => {
const checkboxes = panel.querySelectorAll('.subscription-item input[type="checkbox"]');
checkboxes.forEach((checkbox) => {
checkbox.checked = false;
});
});
const unsubscribeSelectedBtn = panel.querySelector('#unsubscribe-selected');
unsubscribeSelectedBtn.addEventListener('click', async () => {
const checkedItems = panel.querySelectorAll('.subscription-item input[type="checkbox"]:checked');
for (let checkbox of checkedItems) {
const title = checkbox.nextElementSibling.textContent.trim();
await confirmAndUnsubscribe(title);
}
// 刷新订阅
loadAndDisplaySubscriptions();
});
const searchInput = panel.querySelector('#search-input');
searchInput.addEventListener('input', () => {
const searchTerm = searchInput.value.toLowerCase().trim();
const subscriptionItems = panel.querySelectorAll('.subscription-item');
subscriptionItems.forEach((item) => {
const title = item.querySelector('label').textContent.trim().toLowerCase();
if (title.includes(searchTerm)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
});
GM_registerMenuCommand('订阅管理', togglePanel);
})();