NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Remove Streamed YouTube Videos and Highlight Specific Channels // @version 1.1 // @description Remove streamed content from subscriptions page and highlight videos from specific channels // @author nascent // @match https://www.youtube.com/feed/subscriptions // @icon https://www.google.com/s2/favicons?domain=youtube.com // @updateURL https://openuserjs.org/meta/nascent/Remove_Streamed_YouTube_Videos_and_Highlight_Specific_Channels.meta.js // @downloadURL https://openuserjs.org/src/scripts/nascent/Remove_Streamed_YouTube_Videos.user.js // @license GPL-3.0-or-later // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; // Settings let channelsToHighlight = GM_getValue('channelsToHighlight', ['markiplier', 'jacksepticeye']); let highlightColor = GM_getValue('highlightColor', '#faf8cf'); let removeStreamed = GM_getValue('removeStreamed', true); let hideWatched = GM_getValue('hideWatched', false); function saveSettings() { GM_setValue('channelsToHighlight', channelsToHighlight); GM_setValue('highlightColor', highlightColor); GM_setValue('removeStreamed', removeStreamed); GM_setValue('hideWatched', hideWatched); } GM_addStyle(` #channel-manager { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; border: 1px solid #ccc; padding: 20px; z-index: 9999; box-shadow: 0 0 10px rgba(0,0,0,0.5); max-width: 400px; width: 90%; } #channel-manager textarea { width: 100%; height: 100px; margin-bottom: 10px; } #channel-manager .controls { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } #channel-manager .controls > div { display: flex; align-items: center; } #channel-manager .controls label { margin-right: 5px; } #channel-manager .button-row { display: flex; justify-content: space-between; } #close-button, #apply-button { padding: 5px 10px; } `); const observer = new MutationObserver(mutations => { for (let mutation of mutations) { for (let node of mutation.addedNodes) { if (node.nodeName === 'YTD-RICH-ITEM-RENDERER') { handleRenderer(node); setTimeout(() => highlightChannel(node), 500); } } } }); observer.observe(document.body, { childList: true, subtree: true }); function isWatchedVideo(renderer) { const progressBar = renderer.querySelector('ytd-thumbnail-overlay-resume-playback-renderer #progress'); return progressBar !== null; } function handleRenderer(renderer) { if (hideWatched && isWatchedVideo(renderer)) { const videoTitle = renderer.querySelector('#video-title')?.textContent; console.log('Removing watched video:', videoTitle); renderer.remove(); return; } if (removeStreamed) { const metaBlock = renderer.querySelector('ytd-video-meta-block'); if (metaBlock) { const items = metaBlock.querySelectorAll('.inline-metadata-item'); for (let item of items) { if (/^Streamed/.test(item.textContent)) { const videoTitle = renderer.querySelector('#video-title')?.textContent; console.log('Removing streamed video:', videoTitle); renderer.remove(); return; } } } } } function highlightChannel(renderer) { const channelNameElement = renderer.querySelector('#text.ytd-channel-name') || renderer.querySelector('#channel-info #text-container yt-formatted-string'); if (channelNameElement) { const channelName = channelNameElement.textContent.trim(); if (channelsToHighlight.includes(channelName.toLowerCase())) { const detailsContainer = renderer.querySelector('#details') || renderer.querySelector('#meta'); if (detailsContainer) { detailsContainer.style.backgroundColor = highlightColor; } } } } function initialRun() { const initialLoad = document.querySelectorAll('ytd-rich-item-renderer'); initialLoad.forEach(node => { handleRenderer(node); setTimeout(() => highlightChannel(node), 500); }); } setTimeout(initialRun, 1000); function createChannelManager() { const div = document.createElement('div'); div.id = 'channel-manager'; div.innerHTML = ` <h3>Manage Highlighted Channels</h3> <textarea id="channel-textarea" placeholder="Enter channel names, one per line"></textarea> <div class="controls"> <div> <label for="highlight-color">Highlight Color:</label> <input type="color" id="highlight-color" value="${highlightColor}"> </div> <div> <label for="remove-streamed"> <input type="checkbox" id="remove-streamed" ${removeStreamed ? 'checked' : ''}> Remove Streamed Content </label> </div> <div> <label for="hide-watched"> <input type="checkbox" id="hide-watched" ${hideWatched ? 'checked' : ''}> Hide Watched Videos </label> </div> </div> <div class="button-row"> <button id="close-button">Close</button> <button id="apply-button">Apply</button> </div> `; document.body.appendChild(div); const channelTextarea = div.querySelector('#channel-textarea'); const colorInput = div.querySelector('#highlight-color'); const removeStreamedCheckbox = div.querySelector('#remove-streamed'); const hideWatchedCheckbox = div.querySelector('#hide-watched'); const applyButton = div.querySelector('#apply-button'); const closeButton = div.querySelector('#close-button'); // Populate textarea with current channels channelTextarea.value = channelsToHighlight.join('\n'); applyButton.addEventListener('click', () => { // Update channels channelsToHighlight = channelTextarea.value .split('\n') .map(channel => channel.trim().toLowerCase()) .filter(channel => channel !== ''); // Update settings highlightColor = colorInput.value; removeStreamed = removeStreamedCheckbox.checked; hideWatched = hideWatchedCheckbox.checked; saveSettings(); div.remove(); location.reload(); // Reload the page to apply changes }); closeButton.addEventListener('click', () => { div.remove(); // Simply close the dialog without saving changes }); } GM_registerMenuCommand("Customize", createChannelManager); })();