Return YouTube Dislike

Display and interact with dislikes on YouTube videos

// ==UserScript==
// @name         Return YouTube Dislike
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  Display and interact with dislikes on YouTube videos
// @author       MrBlankCoding
// @match        https://www.youtube.com/**
// @match        *://*.youtube.com/*
// @match        *://www.youtube.com/watch**
// @license MIT
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @connect      returnyoutubedislikeapi.com
// ==/UserScript==

(function() {
    'use strict';

    const styles = `
        .dislike-count {
            display: inline-flex;
            align-items: center;
            margin-left: 6px;
            font-size: 14px;
            font-family: "YouTube Sans", Roboto, sans-serif;
            transition: all 0.2s ease;
        }

        .dislike-active {
            color: #ff0000 !important;
            font-weight: 500;
        }

        .dislike-count-wrapper {
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .dislike-animation {
            animation: pulse 0.3s ease-in-out;
        }

        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.2); }
            100% { transform: scale(1); }
        }

        .yt-spec-button-shape-next__icon {
            transition: opacity 0.2s ease;
        }
    `;

    // Add styles to document
    const styleSheet = document.createElement("style");
    styleSheet.textContent = styles;
    document.head.appendChild(styleSheet);

    // Store disliked videos
    const getDislikedVideos = () => GM_getValue('dislikedVideos', {});
    const setDislikedVideos = (videos) => GM_setValue('dislikedVideos', videos);

    let originalIconHTML = null;

    window.addEventListener('yt-navigate-finish', fetchDislikeData);

    function fetchDislikeData() {
        const videoId = new URL(window.location.href).searchParams.get("v");
        if (!videoId) return;

        const apiUrl = `https://returnyoutubedislikeapi.com/votes?videoId=${videoId}`;
        GM_xmlhttpRequest({
            method: "GET",
            url: apiUrl,
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data && data.dislikes !== undefined) {
                        displayDislikeCount(data.dislikes, videoId);
                        setupStateChangeListeners(videoId);
                    }
                } catch (error) {
                    console.error('Error parsing API response:', error);
                }
            },
            onerror: function(error) {
                console.error('Error fetching data from API:', error);
            }
        });
    }

    function displayDislikeCount(dislikes, videoId) {
        const dislikeButton = document.querySelector('dislike-button-view-model');
        if (!dislikeButton) return;

        const button = dislikeButton.querySelector('button');
        if (!button) return;

        // Store original icon if not already stored
        const iconElement = button.querySelector('.yt-spec-button-shape-next__icon');
        if (iconElement && !originalIconHTML) {
            originalIconHTML = iconElement.innerHTML;
        }

        // Remove any existing dislike count
        const existingCount = button.querySelector('.dislike-count');
        if (existingCount) {
            existingCount.remove();
        }

        // Create wrapper for better organization
        const wrapper = document.createElement('div');
        wrapper.className = 'dislike-count-wrapper';

        // Create dislike count element
        const dislikeCountElement = document.createElement('span');
        dislikeCountElement.className = 'dislike-count';
        dislikeCountElement.innerText = roundNumber(dislikes);

        // Check if video is disliked
        const dislikedVideos = getDislikedVideos();
        if (dislikedVideos[videoId]) {
            dislikeCountElement.classList.add('dislike-active');
            if (iconElement) {
                iconElement.style.opacity = '0';
            }
        }

        wrapper.appendChild(dislikeCountElement);
        button.appendChild(wrapper);

        // Add click handler
        button.addEventListener('click', () => handleDislikeClick(videoId, dislikeCountElement, dislikes));
    }

    function setupStateChangeListeners(videoId) {
        // Watch for like button clicks to reset dislike state
        const likeButton = document.querySelector('like-button-view-model button');
        if (likeButton) {
            likeButton.addEventListener('click', () => {
                const dislikedVideos = getDislikedVideos();
                if (dislikedVideos[videoId]) {
                    delete dislikedVideos[videoId];
                    setDislikedVideos(dislikedVideos);
                    resetDislikeUI();
                }
            });
        }
    }

    function handleDislikeClick(videoId, countElement, currentDislikes) {
        const dislikedVideos = getDislikedVideos();
        const isDisliked = dislikedVideos[videoId];

        if (isDisliked) {
            // Undislike
            delete dislikedVideos[videoId];
            resetDislikeUI();
        } else {
            // New dislike
            dislikedVideos[videoId] = true;
            countElement.classList.add('dislike-active');
            countElement.classList.add('dislike-animation');

            const iconElement = document.querySelector('.yt-spec-button-shape-next__icon');
            if (iconElement) {
                iconElement.style.opacity = '0';
            }

            // Remove animation class after it completes
            setTimeout(() => {
                countElement.classList.remove('dislike-animation');
            }, 300);
        }

        setDislikedVideos(dislikedVideos);
    }

    function resetDislikeUI() {
        const countElement = document.querySelector('.dislike-count');
        const iconElement = document.querySelector('.yt-spec-button-shape-next__icon');

        if (countElement) {
            countElement.classList.remove('dislike-active');
        }

        if (iconElement && originalIconHTML) {
            iconElement.style.opacity = '1';
            iconElement.innerHTML = originalIconHTML;
        }
    }

    function roundNumber(num) {
        if (num >= 1000000) {
            return (num / 1000000).toFixed(1) + "M";
        } else if (num >= 1000) {
            return (num / 1000).toFixed(1) + "K";
        }
        return num.toLocaleString();
    }
})();