YouTube Native Language Filter (Embedded)

Фильтр комментариев (Русский/Английский), встроенный в заголовок YouTube

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         YouTube Native Language Filter (Embedded)
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Фильтр комментариев (Русский/Английский), встроенный в заголовок YouTube
// @author       torch
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // --- Настройки стилей ---
    // Используем переменные YouTube для поддержки темной/светлой темы автоматически
    GM_addStyle(`
        #yt-lang-filter-group {
            display: flex;
            align-items: center;
            margin-left: 20px;
            gap: 8px;
        }
        .yt-lang-btn {
            background-color: var(--yt-spec-badge-chip-background, rgba(0, 0, 0, 0.05));
            color: var(--yt-spec-text-primary);
            border: 1px solid transparent;
            border-radius: 16px;
            padding: 0 12px;
            height: 32px;
            font-size: 14px;
            font-family: "Roboto", "Arial", sans-serif;
            font-weight: 500;
            cursor: pointer;
            transition: background-color 0.2s, color 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .yt-lang-btn:hover {
            background-color: var(--yt-spec-button-chip-background-hover, rgba(0, 0, 0, 0.1));
        }
        .yt-lang-btn.active {
            background-color: var(--yt-spec-brand-button-background, #cc0000);
            color: white;
        }
        /* Адаптация под темную тему, если переменные не сработают */
        html[dark] .yt-lang-btn {
            background-color: rgba(255, 255, 255, 0.1);
            color: #f1f1f1;
        }
        html[dark] .yt-lang-btn:hover {
            background-color: rgba(255, 255, 255, 0.2);
        }
        html[dark] .yt-lang-btn.active {
            background-color: #ff4e45;
            color: black;
        }
    `);

    // --- Состояние фильтров ---
    const STATE = {
        hideRussian: false,
        hideEnglish: false
    };

    // --- Определение языка ---
    function detectLanguage(text) {
        if (!text) return 'unknown';
        // Считаем кириллицу и латиницу
        const ruCount = (text.match(/[а-яА-ЯёЁ]/g) || []).length;
        const enCount = (text.match(/[a-zA-Z]/g) || []).length;

        // Если есть кириллица, приоритет ей (даже если есть англ слова типа "wow")
        if (ruCount > 0 && ruCount >= enCount * 0.5) return 'ru'; // *0.5 дает вес русскому
        if (enCount > 0 && enCount > ruCount) return 'en';

        return 'other';
    }

    // --- Функция фильтрации ---
    function applyFilter() {
        // Ищем контейнеры комментариев (поддержка старого и нового дизайна)
        const commentThreads = document.querySelectorAll('ytd-comment-thread-renderer, ytd-comment-view-model');

        commentThreads.forEach(thread => {
            // Ищем сам текст комментария внутри блока
            const contentTextElement = thread.querySelector('#content-text');
            if (!contentTextElement) return;

            const text = contentTextElement.innerText;
            const lang = detectLanguage(text);

            let shouldHide = false;
            if (STATE.hideRussian && lang === 'ru') shouldHide = true;
            if (STATE.hideEnglish && lang === 'en') shouldHide = true;

            thread.style.display = shouldHide ? 'none' : '';
        });
    }

    // --- Создание кнопок ---
    function createButtons() {
        // Проверка, не созданы ли уже кнопки
        if (document.getElementById('yt-lang-filter-group')) return;

        // Находим целевой элемент из вашего HTML (#title)
        const headerTitle = document.querySelector('ytd-comments-header-renderer #title');

        if (!headerTitle) return; // Если заголовок еще не загрузился

        const container = document.createElement('div');
        container.id = 'yt-lang-filter-group';

        // Кнопка для Русского
        const btnRu = document.createElement('button');
        btnRu.className = 'yt-lang-btn';
        btnRu.innerText = 'Скрыть RU';
        btnRu.onclick = () => {
            STATE.hideRussian = !STATE.hideRussian;
            btnRu.classList.toggle('active', STATE.hideRussian);
            applyFilter();
        };

        // Кнопка для Английского
        const btnEn = document.createElement('button');
        btnEn.className = 'yt-lang-btn';
        btnEn.innerText = 'Скрыть EN';
        btnEn.onclick = () => {
            STATE.hideEnglish = !STATE.hideEnglish;
            btnEn.classList.toggle('active', STATE.hideEnglish);
            applyFilter();
        };

        container.appendChild(btnRu);
        container.appendChild(btnEn);

        // Вставляем контейнер в #title
        headerTitle.appendChild(container);
    }

    // --- Наблюдатель за изменениями DOM ---
    function initObserver() {
        const observer = new MutationObserver((mutations) => {
            let shouldRefilter = false;
            let headerFound = false;

            mutations.forEach(mutation => {
                // Если добавились узлы (подгрузка комментариев)
                if (mutation.addedNodes.length) {
                    shouldRefilter = true;
                    // Проверяем, появился ли заголовок, если кнопок еще нет
                    if (!document.getElementById('yt-lang-filter-group')) {
                        createButtons();
                    }
                }
            });

            if (shouldRefilter) {
                applyFilter();
            }
        });

        // Наблюдаем за всем body, так как YouTube - это SPA (Single Page App)
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // --- Запуск ---
    // Используем setInterval для надежности инициализации при навигации
    const intervalId = setInterval(() => {
        const header = document.querySelector('ytd-comments-header-renderer #title');
        if (header) {
            createButtons();
            // Как только нашли заголовок и создали кнопки, можно запускать Observer
            // Но лучше оставить setInterval или Observer работать всегда,
            // т.к. при переходе на другое видео заголовок перерисовывается.
        }
    }, 1000);

    initObserver();

})();