YouTube Native Language Filter (Embedded)

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

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

})();