Greasy Fork

来自缓存

Greasy Fork is available in English.

虎牙重复弹幕过滤

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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