虎牙重复弹幕过滤

虎牙重复弹幕过滤, 过滤视频弹幕, 过滤聊天弹幕, 过滤表情弹幕, 放过自己弹幕

// ==UserScript==
// @name         虎牙重复弹幕过滤
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  虎牙重复弹幕过滤, 过滤视频弹幕, 过滤聊天弹幕, 过滤表情弹幕, 放过自己弹幕
// @author       Mindfulness
// @match        https://www.huya.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const HuyaFilter = {
        selfUserName: '',
        filterCount: 0,
        filterTopList: [],
        recordDanmuList: [],
        _options: {},
        getOptions: function(optionKey, defaultValue)
        {
            if(this._options[optionKey] == undefined)
            {
                this._options[optionKey] = GM_getValue('_huya_filter_' + optionKey, defaultValue);
            }
            return this._options[optionKey];
        },
        setOptions: function(optionKey, optionValue)
        {
            GM_setValue('_huya_filter_' + optionKey, optionValue);
            this._options[optionKey] = optionValue;
        },
        createUI: function()
        {
            let icon = document.createElement('i');
            icon.id = 'J-room-chat-filter';
            icon.className = 'room-chat-tool';
            icon.style = 'display: inline-block;width: 35px;height: 22px;cursor:pointer;text-align:center;margin-top: 1px;border: 1px solid #ff8a00;border-radius: 5px;background-color: #ff8a00;color: #fff;font-weight: bold;user-select:none;';
            icon.innerText = '过滤';

            let toolsPannel = document.querySelector('#tipsOrchat .chat-room__ft .chat-room__ft__pannel .room-chat-tools');
            toolsPannel.appendChild(icon);

            let tipsOrchatRect = document.querySelector('#tipsOrchat').getBoundingClientRect();
            let mainCol = document.querySelector('#main_col');
            let mainColRect = mainCol.getBoundingClientRect();
            let popup = document.createElement('div');
            popup.id = 'J-room-chat-filter-pannel';
            let popTop = tipsOrchatRect.top - mainColRect.top - 300 - 2;
            let popLeft = tipsOrchatRect.left - mainColRect.left;
            popup.style='border:1px solid #333;width:336px;height:300px;background-color:#eee;cursor:default;position:absolute;top:' + popTop + 'px;left:' + popLeft + 'px;z-index:100;display:none;';

            let popupHeader = document.createElement('div');
            popupHeader.style='padding:10px;border-bottom:1px solid #888;';

            let filterCounter = document.createElement('span');
            filterCounter.id = 'J-room-chat-filter-counter';
            filterCounter.style = 'color:#008;';
            filterCounter.innerText = '已过滤: 0';
            popupHeader.appendChild(filterCounter);

            let lastFilterDanmu = document.createElement('span');
            lastFilterDanmu.id = 'J-room-chat-filter-last';
            lastFilterDanmu.style = 'margin-left:10px;padding:0 2px;display:inline-flex;overflow:hidden;max-width:200px;height:18px;color:#aaa;background-color:#ff8;';
            lastFilterDanmu.title = '最后过滤弹幕内容';
            popupHeader.appendChild(lastFilterDanmu);

            let popupClose = document.createElement('a');
            popupClose.innerText = '关闭';
            popupClose.style='cursor:pointer;color:#800;padding-right:10px;position:absolute;right:0;';
            popupClose.addEventListener('click', () => {popup.style.display = 'none'});
            popupHeader.appendChild(popupClose);
            popup.appendChild(popupHeader);

            let popupOptions = document.createElement('div');
            popupOptions.style='padding:10px;border-bottom:1px solid #888;';
            popup.appendChild(popupOptions);

            let popupFilterTopList = document.createElement('div');
            popupFilterTopList.style='padding:10px;';

            let topListElement = document.createElement('ul');
            topListElement.id = 'J-room-chat-filter-topList';
            topListElement.style = 'padding:0;margin:0;';
            popupFilterTopList.appendChild(topListElement);

            let resetElement = document.createElement('a');
            resetElement.innerText = '重置统计';
            resetElement.style = 'cursor:pointer;color:#800;padding-right:10px;position:absolute;right:0;bottom:10px;';
            resetElement.addEventListener('click', function()
            {
                this.filterCount = 0;
                this.filterTopList = [];
                this.recordDanmuList = [];
                this.showFilterCountInfo();
            }.bind(this));
            popupFilterTopList.appendChild(resetElement);

            popup.appendChild(popupFilterTopList);

            mainCol.appendChild(popup);

            icon.addEventListener('click', (e) => {popup.style.display = popup.style.display == 'block' ? 'none' : 'block'});

            this.showCheckboxOptions(popupOptions, 'filter_video_danmu', '过滤视频弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_chat_danmu', '过滤聊天弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_icon_danmu', '过滤表情弹幕');
            this.showCheckboxOptions(popupOptions, 'filter_skip_me', '放过自己发的弹幕');
        },

        showCheckboxOptions: function(contriner, optionKey, optionText)
        {
            let checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.checked = this.getOptions(optionKey, 1) == 1;

            checkbox.addEventListener('change', function(e)
            {
                this.setOptions(optionKey, checkbox.checked ? 1 : 0);
                console.log(optionText + ': ' + (checkbox.checked ? '已开启' : '已关闭'));
            }.bind(this));

            let span = document.createElement('span');
            span.innerText = optionText;
            span.style='padding-left:10px;';

            let label = document.createElement('label');
            label.style = 'display: inline-block;width:45%;';
            label.appendChild(checkbox);
            label.appendChild(span);
            contriner.appendChild(label);
        },

        showFilterCountInfo: function()
        {
            document.querySelector('#J-room-chat-filter-counter').innerHTML = '已过滤: ' + this.filterCount;

            let topListElement = document.querySelector('#J-room-chat-filter-topList');
            topListElement.innerHTML = '';

            for(let i = 0; i < 10; i++)
            {
                let itemElement = document.createElement('li');
                let showIndex = i + 1;
                let itemHtml = '<span style="font-weight:bold;">' + showIndex + '.</span>';
                if(i < this.filterTopList.length) itemHtml += ' (' + this.filterTopList[i].count + ') ' + this.filterTopList[i].danmu;
                itemElement.innerHTML = itemHtml;
                itemElement.style = 'padding:0;margin:0;list-style:none;display:block;width:316px;overflow:hidden;height:18px;line-height:18px;';
                topListElement.appendChild(itemElement);
            }
        },

        start: function()
        {
            let videoDanmuDiv = document.querySelector('#danmudiv');
            let chatDanmuDiv = document.querySelector('#chat-room__list');

            if(!videoDanmuDiv || !chatDanmuDiv) return setTimeout(this.start.bind(this), 1000);

            this.selfUserName = document.querySelector('#login-username').innerText;

            let videoDanmuFilterObserver = new MutationObserver((recordList, observer) => {
                if(this.getOptions('filter_video_danmu', 1) != 1) return;

                let hasNewfilterDanmu = false;
                recordList.forEach((record) => {
                    if(record.type !== 'childList') return;
                    for(let i = 0; i < record.addedNodes.length; i++)
                    {
                        let danmuElement = record.addedNodes[i];
                        if(parseFloat(danmuElement.style.borderWidth) > 0 && this.getOptions('filter_skip_me', 1) == 1) continue;

                        let danmuText = danmuElement.innerText.trim();
                        if(danmuText.length == 0)
                        {
                            if(this.getOptions('filter_icon_danmu', 1) != 1) continue;
                            danmuText = '<表情>';
                        }
                        let filterDanmuText = danmuText;

                        let repeatMatches = danmuText.match(/^(.+?)\1+$/);
                        if(repeatMatches) filterDanmuText = repeatMatches[1] + ' ...';
                        if(!this.recordDanmuList[filterDanmuText]) this.recordDanmuList[filterDanmuText] = 0;
                        let repeatCount = ++this.recordDanmuList[filterDanmuText];
                        if(repeatCount == 1) continue;

                        danmuElement.remove();

                        // 统计新增
                        this.filterCount++;
                        hasNewfilterDanmu = true;

                        // 更新最后过滤弹幕文本
                        document.querySelector('#J-room-chat-filter-last').innerHTML = '(' + repeatCount + ') ' + danmuText;

                        // 追加
                        let alreadyExist = false;
                        for(let i = 0; i < this.filterTopList.length; i++)
                        {
                            if(filterDanmuText === this.filterTopList[i].danmu)
                            {
                                this.filterTopList[i].count = repeatCount;
                                alreadyExist = true;
                                break;
                            }
                        }
                        if(!alreadyExist) this.filterTopList.push({danmu: filterDanmuText, count: repeatCount});
                    }
                });

                if(hasNewfilterDanmu)
                {
                    // 从多到少排序
                    this.filterTopList.sort((a, b) => b.count - a.count);

                    // 删除多余
                    while(this.filterTopList.length > 10) this.filterTopList.pop();

                    // 更新统计信息显示
                    this.showFilterCountInfo();
                }
            });

            videoDanmuFilterObserver.observe(videoDanmuDiv, {childList: true});

            let chatDanmuFilterObserver = new MutationObserver((recordList, observer) => {
                if(this.getOptions('filter_chat_danmu', 1) != 1) return;
                recordList.forEach((record) => {
                    if(record.type !== 'childList') return;
                    for(let i = 0; i < record.addedNodes.length; i++)
                    {
                        let danmuElement = record.addedNodes[i];
                        let danmuUserNameElement = danmuElement.querySelector('.name');
                        // 不是用户发言
                        if(!danmuUserNameElement) continue;

                        let danmuUserName = danmuUserNameElement.innerText;
                        if(danmuUserName == this.selfUserName && this.getOptions('filter_skip_me', 1) == 1) continue;

                        let danmuText = danmuElement.querySelector('.msg').innerText.trim();
                        if(danmuText.length == 0)
                        {
                            if(this.getOptions('filter_icon_danmu', 1) != 1) continue;
                            danmuText = '<表情>';
                        }
                        let filterDanmuText = danmuText;

                        let repeatMatches = danmuText.match(/^(.+?)\1+$/);
                        if(repeatMatches) filterDanmuText = repeatMatches[1] + ' ...';

                        if(!this.recordDanmuList[filterDanmuText]) continue;
                        let repeatCount = this.recordDanmuList[filterDanmuText];
                        if(repeatCount <= 1) continue;

                        danmuElement.remove();
                    }
                });
            });

            chatDanmuFilterObserver.observe(chatDanmuDiv, {childList: true});
        }
    };

    HuyaFilter.start();
    HuyaFilter.createUI();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址