您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hides YouTube videos from specified channels (by handle or channel ID), allows adding via button, and manages list via menu with import/export
当前为
// ==UserScript== // @name YouTube Channel Blocker with Menu and Import/Export // @namespace http://tampermonkey.net/ // @version 0.7 // @description Hides YouTube videos from specified channels (by handle or channel ID), allows adding via button, and manages list via menu with import/export // @author DoctorEye // @license MIT // @match https://www.youtube.com/* // @grant GM_addStyle // @grant GM_registerMenuCommand // ==/UserScript== (function() { 'use strict'; // Initial list of blocked channels (handles or channel IDs) const initialBlockedChannels = [ '@AutoVortex-01', '@RevReviewtrend', '@TurboFusion-35' ].map(item => item.toLowerCase()); // Load blocked channels from localStorage or use initial list let blockedChannels = JSON.parse(localStorage.getItem('blockedChannels')) || initialBlockedChannels; if (!localStorage.getItem('blockedChannels')) { localStorage.setItem('blockedChannels', JSON.stringify(blockedChannels)); } // CSS for Block Channel button and UI GM_addStyle(` .block-channel-btn { background-color: #ff4444; color: white; border: none; padding: 5px 10px; margin-left: 10px; cursor: pointer; border-radius: 3px; font-size: 12px; } .block-channel-btn:hover { background-color: #cc0000; } #blocker-ui { position: fixed; top: 20%; left: 50%; transform: translateX(-50%); background: white; border: 1px solid #ccc; padding: 20px; z-index: 1000; box-shadow: 0 0 10px rgba(0,0,0,0.5); display: none; } #blocker-ui textarea { width: 300px; height: 150px; } #blocker-ui button { margin: 10px 5px; } `); // Save blocked channels to localStorage function saveBlockedChannels() { localStorage.setItem('blockedChannels', JSON.stringify(blockedChannels)); } // Function to hide videos and add Block button function hideVideos() { const videoElements = document.querySelectorAll( 'ytd-video-renderer, ytd-grid-video-renderer, ytd-rich-item-renderer, ytd-compact-video-renderer, ytd-reel-item-renderer, ytd-playlist-renderer' ); videoElements.forEach(video => { // Check for channel links const channelElements = video.querySelectorAll('a[href*="/@"], a[href*="/channel/"], a[href*="/user/"], ytd-channel-name a'); let foundIdentifier = null; for (const element of channelElements) { const href = element.href || ''; // Extract handle from @ links const handleMatch = href.match(/\/@[^\/]+/)?.[0]?.toLowerCase(); // Extract channel ID from /channel/ or /user/ links const channelIdMatch = href.match(/\/(channel|user)\/([^\/?]+)/)?.[2]?.toLowerCase(); if (handleMatch && blockedChannels.includes(handleMatch)) { foundIdentifier = handleMatch; video.style.display = 'none'; console.log(`Blocked video from handle: ${handleMatch}`); break; } else if (channelIdMatch && blockedChannels.includes(channelIdMatch)) { foundIdentifier = channelIdMatch; video.style.display = 'none'; console.log(`Blocked video from channel ID: ${channelIdMatch}`); break; } else { // Log for debugging if (handleMatch) { console.log(`Detected handle (not blocked): ${handleMatch}`); } else if (channelIdMatch) { console.log(`Detected channel ID (not blocked): ${channelIdMatch}`); } } } // Add Block Channel button if not already present if (!video.querySelector('.block-channel-btn')) { const channelLink = video.querySelector('a[href*="/@"], a[href*="/channel/"], a[href*="/user/"]'); if (channelLink) { const handle = channelLink.href.match(/\/@[^\/]+/)?.[0]; const channelId = channelLink.href.match(/\/(channel|user)\/([^\/?]+)/)?.[2]; const identifier = handle || channelId; if (identifier) { const button = document.createElement('button'); button.className = 'block-channel-btn'; button.textContent = 'Block Channel'; button.onclick = () => { if (!blockedChannels.includes(identifier.toLowerCase())) { blockedChannels.push(identifier.toLowerCase()); saveBlockedChannels(); hideVideos(); alert(`Blocked: ${identifier}`); } else { alert(`${identifier} is already blocked.`); } }; const metaContainer = video.querySelector('#meta') || video; metaContainer.appendChild(button); } else { console.log(`No identifier found for channel link: ${channelLink.href}`); } } } }); } // Create UI for managing blocked channels function createManageUI() { let ui = document.getElementById('blocker-ui'); if (!ui) { ui = document.createElement('div'); ui.id = 'blocker-ui'; ui.innerHTML = ` <h3>Manage Blocked Channels</h3> <textarea id="blocked-channels-list">${blockedChannels.join('\n')}</textarea> <br> <input type="file" id="import-channels" accept=".json" style="margin: 10px 0;"> <br> <button id="save-channels">Save</button> <button id="export-channels">Export</button> <button id="clear-channels">Clear List</button> <button id="close-ui">Close</button> `; document.body.appendChild(ui); // Event listeners for buttons document.getElementById('save-channels').onclick = () => { const newList = document.getElementById('blocked-channels-list').value .split('\n') .map(line => line.trim().toLowerCase()) .filter(line => line.startsWith('@') || line.match(/^[a-z0-9_-]+$/i)); blockedChannels = [...new Set(newList)]; saveBlockedChannels(); hideVideos(); alert('List updated!'); ui.style.display = 'none'; }; document.getElementById('export-channels').onclick = () => { const data = JSON.stringify(blockedChannels, null, 2); const blob = new Blob([data], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'blocked_channels.json'; a.click(); URL.revokeObjectURL(url); }; document.getElementById('import-channels').onchange = (event) => { const file = event.target.files[0]; if (!file) { alert('No file selected.'); return; } const reader = new FileReader(); reader.onload = (e) => { try { const importedList = JSON.parse(e.target.result); if (Array.isArray(importedList) && importedList.every(item => typeof item === 'string' && (item.startsWith('@') || item.match(/^[a-z0-9_-]+$/i)))) { blockedChannels = [...new Set(importedList.map(item => item.toLowerCase()))]; saveBlockedChannels(); document.getElementById('blocked-channels-list').value = blockedChannels.join('\n'); hideVideos(); alert('Channels imported successfully!'); } else { alert('Invalid file format. Must be a JSON array of channel handles (starting with @) or channel IDs.'); } } catch (error) { alert('Error importing file: ' + error.message); } }; reader.onerror = () => { alert('Error reading file.'); }; reader.readAsText(file); }; document.getElementById('clear-channels').onclick = () => { if (confirm('Are you sure you want to clear the blocked channels list?')) { blockedChannels = []; saveBlockedChannels(); document.getElementById('blocked-channels-list').value = ''; hideVideos(); alert('List cleared!'); } }; document.getElementById('close-ui').onclick = () => { ui.style.display = 'none'; }; } ui.style.display = 'block'; } // Register menu command for managing blocked channels GM_registerMenuCommand('Manage Blocked Channels', createManageUI, 'm'); // Initial execution hideVideos(); // Observe DOM changes const observer = new MutationObserver(hideVideos); observer.observe(document.body, { childList: true, subtree: true }); // Re-run every 0.5 seconds for late-loaded content setInterval(hideVideos, 500); })();