您需要先安装一个扩展,例如 篡改猴、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);
- })();