YouTube Auto Dislike

Because YouTube no longer displays the number of dislikes, the scripts automatically dislike all videos. Wait 15 seconds to see the effect.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name  YouTube Auto Dislike
// @namespace  gqqnbig
// @match  https://www.youtube.com/*
// @grant  none
// @version  1.5
// @author  gqqnbig
// @description  Because YouTube no longer displays the number of dislikes, the scripts automatically dislike all videos. Wait 15 seconds to see the effect.
// @license  GNU General Public License v3.0
// ==/UserScript==


(function () {
    "use strict";

    //from https://github.com/Anarios/return-youtube-dislike/blob/main/Extensions/UserScript/Return%20Youtube%20Dislike.user.js
    const LIKED_STATE = "LIKED_STATE";
    const DISLIKED_STATE = "DISLIKED_STATE";
    const NEUTRAL_STATE = "NEUTRAL_STATE";

    let isShorts = () => location.pathname.startsWith("/shorts");


    function isInViewport(element) {
        const rect = element.getBoundingClientRect();
        const height = innerHeight || document.documentElement.clientHeight;
        const width = innerWidth || document.documentElement.clientWidth;
        return (
            // When short (channel) is ignored, the element (like/dislike AND short itself) is
            // hidden with a 0 DOMRect. In this case, consider it outside of Viewport
            !(rect.top == 0 && rect.left == 0 && rect.bottom == 0 && rect.right == 0) &&
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= height &&
            rect.right <= width
        );
    }

    function getButtons() {
        if (isShorts()) {
            let elements = document.querySelectorAll("#like-button > ytd-like-button-renderer");
            for (let element of elements) {
                if (isInViewport(element)) {
                    return element;
                }
            }
        }
        if (document.getElementById("menu-container")?.offsetParent === null) {
            return (
                document.querySelector("ytd-menu-renderer.ytd-watch-metadata > div") ??
                document.querySelector("ytd-menu-renderer.ytd-video-primary-info-renderer > div")
            );
        } else {
            return document
                .getElementById("menu-container")
                ?.querySelector("#top-level-buttons-computed");
        }
    }

    function getDislikeButton() {
        if (getButtons().children[0].tagName ===
            "YTD-SEGMENTED-LIKE-DISLIKE-BUTTON-RENDERER") {
            if (getButtons().children[0].children[1] === undefined) {
                return document.querySelector("#segmented-dislike-button");
            } else {
                return getButtons().children[0].children[1];
            }
        } else {
            if (getButtons().querySelector("segmented-like-dislike-button-view-model")) {
                const dislikeViewModel = getButtons().querySelector("dislike-button-view-model");
                return dislikeViewModel;
            } else {
                return getButtons().children[1];
            }
        }
    }

    function getLikeButton() {
        return getButtons().children[0].tagName ===
        "YTD-SEGMENTED-LIKE-DISLIKE-BUTTON-RENDERER"
            ? document.querySelector("#segmented-like-button") !== null ? document.querySelector("#segmented-like-button") : getButtons().children[0].children[0]
            : getButtons().querySelector("like-button-view-model") ?? getButtons().children[0];
    }

    function isVideoLiked() {
        return getLikeButton().querySelector('button').getAttribute('aria-pressed') === 'true';
    }

    function isVideoDisliked() {
        return getDislikeButton().querySelector('button').getAttribute('aria-pressed') === 'true';
    }

    function getState() {
        if (isVideoLiked()) {
            return LIKED_STATE;
        }
        if (isVideoDisliked()) {
            return DISLIKED_STATE;
        }
        return NEUTRAL_STATE;
    }


    function isWatchPage() {
        return window.location.pathname.startsWith('/watch');
    }

    const observer = new MutationObserver((mutations) => {
        if (mutations.length > 0 && mutations[0].addedNodes.length > 0 && mutations[0].addedNodes[0].wholeText !== 'YouTube' &&
            isWatchPage()) {
            console.log('start unlike');
            // observer.disconnect(); // Stop observing temporarily

            setTimeout(() => {
                if (getState() === NEUTRAL_STATE) {
                    getDislikeButton().querySelector('button').click();
                }
            }, 15000);

            // observer.observe(head, config);
        }
    });

    // only watch the direct children
    const config = {childList: true};

    let head;
    let getTitleHandle = setInterval(() => {
        head = document.head.querySelector('title');
        if (head) {
            observer.observe(head, config);
            clearInterval(getTitleHandle);
        }
    }, 200);
})();