Raw Source
nascent / Remove Streamed YouTube Videos and Highlight Specific Channels

// ==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);
})();