Greasy Fork is available in English.

Discussions » Development

pls help fixing this youtube remove from watch later button code

§
Posted: 11-03-2024

I am working on a script: when user is visiting youtube watch later list page and mouse hovering on any video's thumbnail, there will show a button, which can remove this video from watch later list.

With the following code's 2 versions, I can see the button when hovering on video thumbnail. But nothing happens after pressing this button. I admit this is beyond my level and these're all from google searches and chatgpt. Please help.

one version:

// ==UserScript==
// @name         YouTube Watch Later Remove Button on Hover
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Show a remove button on hover over video thumbnails in the Watch Later list
// @author       me
// @license MIT
// @match        https://www.youtube.com/playlist?list=WL
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    if (!window.location.href.includes('list=WL')) return;

    function createRemoveButton(videoElement) {
        let removeButton = document.createElement('button');
        removeButton.textContent = 'Remove';
        removeButton.style.position = 'absolute';
        removeButton.style.top = '5px';
        removeButton.style.right = '5px';
        removeButton.style.zIndex = '1000';

        removeButton.onclick = function() {
            let menuButton = videoElement.querySelector('button[aria-label="Action menu"]');
            if (menuButton) {
                menuButton.click();

                // MutationObserver to detect when the removal option appears
                let observer = new MutationObserver(function(mutations, obs) {
                    let removeOption = Array.from(document.querySelectorAll('ytd-menu-service-item-renderer'))
                                            .find(el => el.textContent.includes('Remove from Watch Later'));
                    if (removeOption) {
                        removeOption.click();
                        obs.disconnect(); // Stop observing after clicking remove
                    }
                });

                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }
        };

        videoElement.appendChild(removeButton);
    }

    document.addEventListener('mouseover', function(e) {
        let videoElement = e.target.closest('ytd-thumbnail');
        if (videoElement && !videoElement.querySelector('button')) {
            createRemoveButton(videoElement);
        }
    });
})();

Another version:

// ==UserScript==
// @name         YouTube Watch Later Remove Button on Hover
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Show a remove button on hover over video thumbnails in the Watch Later list
// @author       me
// @license MIT
// @match        https://www.youtube.com/playlist?list=WL
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    if (!window.location.href.includes('list=WL')) return;

    function createRemoveButton(videoElement) {
        let removeButton = document.createElement('button');
        removeButton.textContent = 'Remove';
        removeButton.style.position = 'absolute';
        removeButton.style.top = '5px';
        removeButton.style.right = '5px';
        removeButton.style.zIndex = '1000';

        removeButton.onclick = function(e) {
            e.preventDefault(); // Prevent the default action of the event
            e.stopPropagation(); // Stop the event from bubbling up
            e.stopImmediatePropagation(); // Prevents other listeners of the same event from being called

            let menuButton = videoElement.querySelector('button[aria-label="Action menu"]');
            if (menuButton) {
                menuButton.click();
                // The rest of your logic for clicking the remove option
            }
        };



        videoElement.appendChild(removeButton);
    }

    document.addEventListener('mouseover', function(e) {
        let videoElement = e.target.closest('ytd-thumbnail');
        if (videoElement && !videoElement.querySelector('button')) {
            createRemoveButton(videoElement);
        }
    });
})();

§
Posted: 11-03-2024
Edited: 11-03-2024

If you click the "Watch Later" button (icon is "clock"), it will turn into "cancel button" (icon is "tick")

Just click the cancel button to remove it from watch later

img

img

For "https://www.youtube.com/playlist?list=WL" page,

§
Posted: 11-03-2024
Edited: 11-03-2024

Should work on Violentmonkey / Tampermonkey

// ==UserScript==
// @name         YouTube Watch Later Remove Button on Hover
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Show a remove button on hover over video thumbnails in the Watch Later list
// @author       me
// @license MIT
// @match        https://www.youtube.com/*
// @run-at              document-start
// @grant               none
// @inject-into         page
// ==/UserScript==

(function () {
    'use strict';

    let scriptEnable = false;

    let mouseenterHandler = null;
    let mouseleaveHandler = null;

    document.addEventListener('yt-navigate-finish', () => {
        scriptEnable = false;

        if (!mouseenterHandler) return;

        document.removeEventListener('mouseenter', mouseenterHandler, true);
        document.removeEventListener('mouseleave', mouseleaveHandler, true);

        if (location.pathname !== '/playlist') return;
        if (!location.search.includes('list=WL')) return;

        scriptEnable = true;

        document.addEventListener('mouseenter', mouseenterHandler, true);
        document.addEventListener('mouseleave', mouseleaveHandler, true);

    }, false);

    const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);

    function getItemData(menu) {
        if (!menu) return;
        let items = ((menu || 0).menuRenderer || 0).items || 0;
        if (!(items.length >= 1)) return;
        const filtered = items.filter((entry) => {
            if (!entry || typeof entry !== 'object') return false;
            try {
                const t = (JSON.stringify(entry) || '');
                return t.includes('ACTION_REMOVE_VIDEO');
            } catch (e) {
                return false;
            }
        });
        if (filtered.length !== 1) return;
        let itemData = filtered[0];
        itemData = Object.values(itemData).filter(e => e.serviceEndpoint || e.command)[0];
        if (!itemData) return;
        return itemData;
    }

    function createRemoveButton(videoElement, renderer) {
        let removeButton = document.createElement('button');
        removeButton.className = 'watch-later-remove-button';
        removeButton.textContent = 'Remove';
        removeButton.style.position = 'absolute';
        removeButton.style.top = '5px';
        removeButton.style.right = '5px';
        removeButton.style.zIndex = '1000';

        removeButton.onclick = function (e) {

            if (!scriptEnable) return;

            let data = insp(renderer).data || 0;
            const itemData = getItemData(data.menu);
            if (!itemData) return;

            e.preventDefault(); // Prevent the default action of the event
            e.stopPropagation(); // Stop the event from bubbling up
            e.stopImmediatePropagation(); // Prevents other listeners of the same event from being called

            let dummy = document.createElement('ytd-menu-service-item-renderer');
            // cnt.hostElement.appendChild(dummy);

            (function (itemData) {

                this.data = Object.assign({}, itemData);

                const a = this.data.serviceEndpoint;
                const b = this.data.command;

                a && this.ytComponentBehavior.resolveCommand(a);
                b && this.ytComponentBehavior.resolveCommand(b);

                this.data = null;

            }).call(insp(dummy), itemData);

            dummy = null;
        };
        videoElement.appendChild(removeButton);
    }

    mouseenterHandler = function (e) {
        if (!e || !(e.target instanceof HTMLElement)) return;
        if (!scriptEnable) return;
        if (!e.target.classList.contains('ytd-playlist-video-renderer')) return;
        let videoElement = e.target.closest('ytd-thumbnail');
        if (!videoElement) return;
        let renderer = videoElement.closest('ytd-playlist-video-renderer.style-scope.ytd-playlist-video-list-renderer[is-editable]');
        if (!renderer || renderer.is !== 'ytd-playlist-video-renderer') return;
        let data = insp(renderer).data || 0;
        let itemData = getItemData(data.menu);
        if (!itemData) return;
        let button = videoElement.querySelector('button.watch-later-remove-button')
        if (!button) {
            createRemoveButton(videoElement, renderer);
        } else {
            button.style.display = '';
        }
    };

    mouseleaveHandler = function (e) {
        if (!e || !(e.target instanceof HTMLElement)) return;
        if (!e.target.classList.contains('ytd-playlist-video-renderer')) return;
        let button = e.target.querySelector('button.watch-later-remove-button');
        if (button) {
            button.style.display = 'none';
        }
    };

})();
§
Posted: 12-03-2024

Should work on Violentmonkey / Tampermonkey

// ==UserScript==
// @name         YouTube Watch Later Remove Button on Hover
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Show a remove button on hover over video thumbnails in the Watch Later list
// @author       me
// @license MIT
// @match        https://www.youtube.com/*
// @run-at              document-start
// @grant               none
// @inject-into         page
// ==/UserScript==

(function () {
    'use strict';

    let scriptEnable = false;

    let mouseenterHandler = null;
    let mouseleaveHandler = null;

    document.addEventListener('yt-navigate-finish', () => {
        scriptEnable = false;

        if (!mouseenterHandler) return;

        document.removeEventListener('mouseenter', mouseenterHandler, true);
        document.removeEventListener('mouseleave', mouseleaveHandler, true);

        if (location.pathname !== '/playlist') return;
        if (!location.search.includes('list=WL')) return;

        scriptEnable = true;

        document.addEventListener('mouseenter', mouseenterHandler, true);
        document.addEventListener('mouseleave', mouseleaveHandler, true);

    }, false);

    const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);

    function getItemData(menu) {
        if (!menu) return;
        let items = ((menu || 0).menuRenderer || 0).items || 0;
        if (!(items.length >= 1)) return;
        const filtered = items.filter((entry) => {
            if (!entry || typeof entry !== 'object') return false;
            try {
                const t = (JSON.stringify(entry) || '');
                return t.includes('ACTION_REMOVE_VIDEO');
            } catch (e) {
                return false;
            }
        });
        if (filtered.length !== 1) return;
        let itemData = filtered[0];
        itemData = Object.values(itemData).filter(e => e.serviceEndpoint || e.command)[0];
        if (!itemData) return;
        return itemData;
    }

    function createRemoveButton(videoElement, renderer) {
        let removeButton = document.createElement('button');
        removeButton.className = 'watch-later-remove-button';
        removeButton.textContent = 'Remove';
        removeButton.style.position = 'absolute';
        removeButton.style.top = '5px';
        removeButton.style.right = '5px';
        removeButton.style.zIndex = '1000';

        removeButton.onclick = function (e) {

            if (!scriptEnable) return;

            let data = insp(renderer).data || 0;
            const itemData = getItemData(data.menu);
            if (!itemData) return;

            e.preventDefault(); // Prevent the default action of the event
            e.stopPropagation(); // Stop the event from bubbling up
            e.stopImmediatePropagation(); // Prevents other listeners of the same event from being called

            let dummy = document.createElement('ytd-menu-service-item-renderer');
            // cnt.hostElement.appendChild(dummy);

            (function (itemData) {

                this.data = Object.assign({}, itemData);

                const a = this.data.serviceEndpoint;
                const b = this.data.command;

                a && this.ytComponentBehavior.resolveCommand(a);
                b && this.ytComponentBehavior.resolveCommand(b);

                this.data = null;

            }).call(insp(dummy), itemData);

            dummy = null;
        };
        videoElement.appendChild(removeButton);
    }

    mouseenterHandler = function (e) {
        if (!e || !(e.target instanceof HTMLElement)) return;
        if (!scriptEnable) return;
        if (!e.target.classList.contains('ytd-playlist-video-renderer')) return;
        let videoElement = e.target.closest('ytd-thumbnail');
        if (!videoElement) return;
        let renderer = videoElement.closest('ytd-playlist-video-renderer.style-scope.ytd-playlist-video-list-renderer[is-editable]');
        if (!renderer || renderer.is !== 'ytd-playlist-video-renderer') return;
        let data = insp(renderer).data || 0;
        let itemData = getItemData(data.menu);
        if (!itemData) return;
        let button = videoElement.querySelector('button.watch-later-remove-button')
        if (!button) {
            createRemoveButton(videoElement, renderer);
        } else {
            button.style.display = '';
        }
    };

    mouseleaveHandler = function (e) {
        if (!e || !(e.target instanceof HTMLElement)) return;
        if (!e.target.classList.contains('ytd-playlist-video-renderer')) return;
        let button = e.target.querySelector('button.watch-later-remove-button');
        if (button) {
            button.style.display = 'none';
        }
    };

})();

Works like a charm! Much appreciated!

Post reply

Sign in to post a reply.