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와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 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();

})();