直播插件

虎牙直播、斗鱼直播 页面简化,屏蔽主播直播间

16.12.2022 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

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

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

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.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         直播插件
// @namespace    http://tampermonkey.net/
// @version      3.3
// @description  虎牙直播、斗鱼直播 页面简化,屏蔽主播直播间
// @author       wuxin001
// @match        https://www.huya.com/*
// @match        https://www.douyu.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==
(function () {
    'use strict';
    const huya_address_pattern = /^https:\/\/.*\.huya\.((com)|(cn)).*/
    const doyu_address_pattern = /^https:\/\/.*\.douyu\.((com)|(cn)).*/
    // const bg_regx = /^http[s](.*)(\.(png|jpg|jpeg).*)$/;
    const bg_regx = /.*/;
    const local_url = window.location.href
    const is_huya = huya_address_pattern.test(local_url) // 是否是虎牙地址
    const is_douyu = doyu_address_pattern.test(local_url) // 是否是斗鱼地址
    const wd = window.document
    const wls = window.localStorage // 简化存储对象
    const download_plugin_url =
          'https://greatest.deepsurf.us/zh-CN/scripts/449261-%E8%99%8E%E7%89%99%E7%9B%B4%E6%92%AD' // 下载地址
    const source_code_url = 'https://github.com/wuxin0011/huya-live' // 源码地址

    const time = 2000 //延迟时间

    /**
     * 页面加载完成调用该方法!
     */
    window.onload = () => {
        setTimeout(() => {
            try {
                let text = is_huya ? '虎牙' : '斗鱼'
                text = '%c欢迎使用' + text + '直播插件,下载地址%c'
                console.log(
                    text
                    .concat(download_plugin_url, ''),
                    'background: rgb(255, 93, 35); padding: 1px; border-radius: 3px 0 0 3px; color: #fff',
                    'border-radius: 0 3px 3px 0; color: #fff')
                console.log(
                    '%c源码地址:%c '
                    .concat(source_code_url, ''),
                    'background: rgb(255, 93, 35); padding: 1px; border-radius: 3px 0 0 3px; color: #fff',
                    'border-radius: 0 3px 3px 0; color: #fff')

                //插件执行入口
                if (is_huya) {
                    // 执行虎牙直播插件
                    new TriggerLive()
                } else if (is_douyu) {
                    // 执行斗鱼直播插件
                    new FishLive()
                } else {
                    console.warn('插件地址不适配,请检查匹配地址!!!')
                }
            } catch (e) {

            }

        }, time)
    }



    /**
     * 日志输出
     */
    const log = (msg, level = 'log') => {
        try {
            if (level == 'log') {
                console.log(new Date().toLocaleString(), msg);
            }
            if (level == 'info') {
                console.info(new Date().toLocaleString(), msg);
            }
            if (level == 'warn') {
                console.warn(new Date().toLocaleString(), msg);
            }
            if (level == 'error') {
                console.error(new Date().toLocaleString(), msg);
            }
        } catch (e) {
            console.log(e)
        }
    }


    /**
     * 主播类
     */
    class HostUser {
        constructor(roomId, name) {
            this.roomId = roomId;
            this.name = name;
        }
    }


    /**
     * 直播插件,要求所有直播插件继承该类,并实现要求重写的方法!
     */
    class LivePlugin {
        constructor() {
            // 存放内容信息
            this.key = 'key'
            // 存放背景图
            this.bg_key = 'bg_key'
            // 是否显示背景key
            this.bg_show_key = 'bg_show_key'
            // 是否显示菜单
            this.menu_show_key = 'menu_show_key'
            // 直播源
            this.baseUrl = "baseUrl"
            // 默认背景图
            this.defaultBackgroundImage = 'defaultBackgroundImage'
            // 存放屏蔽主播信息
            this.users = []
            // body
            this.html = null
            // body
            this.body = null
            // 菜单
            this.menu = null
            // 操作数据
            this.tbody = null
            // 操作容器
            this.m_container = null
        }

        // 初始化操作方法,子类可以继承该类,实现该类中空方法,参考此操作,初始化构造器实调用该方法就可以了。。。
        init() {
            if (!this.removeRoom()) {
                this.detail()
                this.common()
                this.index()
                this.category()
                this.create_container()
            }
            // 设置壁纸
            this.settingBackgroundImage()
            // 设置菜单
            this.loadLeftMenu()
        }


        /*********************************建议下面操作方法必须重写的,并且参考此步骤*****************************/

        // 公共部分页面操作
        common() {}
        //首页操作
        index() {}
        // 分类页面操作
        category() {}
        // 详情页操作
        detail() {}
        // 通过点击直播间名称删除直播间
        removeRoomByClickRoomName() {}
        // 通过房间号获取直播间name
        getNameByRoomId(roomId) {
            alert('该操作未实现!');
            return null
        }
        // 通过房间地址获取房间号
        getRoomIdByUrl(url) {
            return null
        }

        /*********************************下面方法不建议重写******************************/


        /**
         * 容器,所有操作容器均在此容器中,
         */
        create_container() {
            // 初始化房间号
            let that = this
            if (!that.body || !that.html) {
                that.html = wd.querySelector('html')
                that.body = wd.querySelector('body')
            }
            if (!that.body) {
                that.body = wd.createElement('body')
            }
            that.users = that.getLocalStore(that.key, Array.name)
            let show1 = that.getLocalStore(that.bg_key, Boolean.name)
            let show2 = that.getLocalStore(that.menu_show_key, Boolean.name)
            that.m_container = that.s2d(`
		                     <div class="m-container">
		                     <div class="operation">
		                          <input type="text" placeholder="房间号或者名称...">
		                           <button class="btn btn-success search-room">搜索</button>
		                           <button class="btn btn-teal add-room">添加</button>
		                           <button class="btn btn-info flush-room">刷新</button>
		                           <button class="btn btn-danger clear-room">重置</button>
                                   <button class="btn btn-warning bg-btn">上传</button>
		                           <input type="file" id="file">
		                           <input type="checkbox" id="checkbox1" checked=${show1} class="checkbox"/>背景
		                           <input type="checkbox" id="checkbox2" checked=${show2} class="checkbox"/>菜单
		                           <a class="m-link" href="https://greatest.deepsurf.us/zh-CN/scripts/449261-%E8%99%8E%E7%89%99%E7%9B%B4%E6%92%AD" target="_blank" title="更新、反馈">更新</a>
		                       </div>
		                      <table >
		                           <thead>
		                             <th>序号</th>
		                             <th>名称</th>
		                             <th>房间号</th>
		                             <th>操作</th>
		                           </thead>
		                           <tbody>
		                           </tbody>
		                       </table>
		                     </div>
		 `)



            that.body.appendChild(that.m_container)
            that.tbody = that.m_container.querySelector('.m-container table tbody')
            // 生成操作按钮
            that.operationDOMButton()
            // 添加直播房间号信息
            that.createRoomItem(that.users)
            // 右侧点击添加button
            that.createButton()

        }


        /**
         * 通过用户列表构建列表
         * @param {Object} arr  用户列表
         */
        createRoomItem(arr) {
            if (!Array.isArray(arr)) {
                return;
            }
            let that = this
            arr.forEach((item, index) => {
                let tr = wd.createElement('tr')
                tr.style.borderBottom = '1px solid rgba(0,0,0,0.4)'
                tr.style.margin = '10px 0'
                tr.style.padding = '20px 10px'
                tr.innerHTML =
                    `<td style="padding:10px;">${index+1}</td>
		                  <td style="padding:10px;">${item.name}</td>
		                  <td style="padding:10px;">${item.roomId}</td>
		                  <td style="padding:10px;">
		                  <button class="btn btn-danger" room-id="${item.roomId}">删除</button></td>`
                that.tbody.appendChild(tr)
                // 添加删除事件
                const deleteBtn = tr.querySelector('button')
                deleteBtn.addEventListener('click', function (e) {
                    let roomId = e.target.getAttribute('room-id');
                    that.userDelete(roomId)
                    // 如果是当前主播,需要刷新
                    if (that.getRoomIdByUrl(local_url) == roomId) {
                        window.location.reload()
                    }
                    that.removeDOM(tr)
                })

            })
        }


        /**
         * 解析DOM字符串
         * @param {Object} string DOM文档树
         */
        s2d(string) {
            return new DOMParser().parseFromString(string, 'text/html').body.childNodes[0]
        }


        /**
         * 绘制表格
         * @param {Object} arr 表格数据
         */
        resetTbody(arr) {
            // 删除原来dom
            this.removeDOM(this.tbody, true)
            let table = this.m_container.querySelector('.m-container table')
            this.tbody = wd.createElement('tbody')
            let thead = wd.createElement('thead')
            let room_index = wd.createElement('th')
            let room_name = wd.createElement('th')
            let room_id = wd.createElement('th')
            let room_operation = wd.createElement('th')
            thead.appendChild(room_index)
            thead.appendChild(room_name)
            thead.appendChild(room_id)
            thead.appendChild(room_operation)
            table.appendChild(this.tbody)
            // 添加操作窗口
            this.createRoomItem(arr)

        }


        /**
         * 操作框容器
         */
        operationDOMButton() {
            let that = this
            if (!that.m_container) {
                return;
            }
            const inputValue = that.m_container.querySelector('.m-container .operation input')
            if (inputValue) {
                // 输入框
                inputValue.addEventListener('keyup', function (e) {
                    if (e.key == 'Enter') {
                        let arr = that.users.filter(item => {
                            return (item.roomId && item.roomId.indexOf(inputValue.value) != -
                                    1) || (item.name && item.name.indexOf(inputValue.value) != -1)
                        })
                        that.resetTbody(arr)
                    }
                })
            }

            // 添加
            const addRoomBtn = that.m_container.querySelector('.m-container .operation  button.add-room')
            if (addRoomBtn) {
                addRoomBtn.addEventListener('click', function () {
                    const keywords = inputValue.value.trim()
                    if (!keywords) {
                        return alert('请输入房间号!')
                    }
                    if (!that.userIsExist(keywords)) {
                        const name = that.getNameByRoomId(keywords)
                        if (name) {
                            that.addUser(keywords, name)
                            inputValue.value = ''
                        } else {
                            if (confirm(`房间号为${keywords}的主播不存在!确定添加?`)) {
                                that.addUser(keywords, keywords)
                                inputValue.value = ''
                            }
                        }

                    } else {
                        alert('该主播已添加!')
                    }
                })

            }

            // 刷新
            const flushRoomBtn = that.m_container.querySelector('.m-container button.flush-room')
            if (flushRoomBtn) {
                flushRoomBtn.addEventListener('click', function () {
                    that.users = that.getLocalStore()
                    that.resetTbody(that.users)
                })
            }


            // 搜索
            const searchRoomBtn = that.m_container.querySelector('.m-container .operation .search-room')
            if (searchRoomBtn) {
                searchRoomBtn.addEventListener('click', function () {
                    let arr = that.users.filter(item => {
                        return (item.roomId && item.roomId.indexOf(inputValue.value) != -1) || (
                            item.name && item.name.indexOf(inputValue.value) != -1)
                    })
                    that.resetTbody(arr)
                })
            }

            // 清空
            const clearRoomBtn = that.m_container.querySelector('.m-container button.clear-room')
            if (clearRoomBtn) {
                clearRoomBtn.addEventListener('click', function () {
                    if (confirm('确认重置?')) {
                        that.users = []
                        wls.removeItem(that.key)
                        wls.removeItem(that.bg_key)
                        wls.removeItem(that.menu_show_key)
                        that.resetTbody(that.users)
                        window.location.reload()
                    }
                })
            }

            // 文件上传
            const uploadButton = that.m_container.querySelector('.m-container #file')
            if (uploadButton) {
                uploadButton.addEventListener('change', function (e) {
                    const file = uploadButton.files[0] || null
                    if (file) {
                        let fileReader = new FileReader()
                        fileReader.onload = (e) => {
                            // 保存到本地
                            that.addLocalStore(that.bg_key, e.target.result, String.name, false)
                            that.settingBackgroundImage(e.target.result)
                        }
                        // 转码
                        fileReader.readAsDataURL(file)
                    }
                })
            }

              // 文件上传
            const upload= that.m_container.querySelector('.m-container .bg-btn')
            if (upload) {
                upload.addEventListener('click', function (e) {
                   uploadButton.click()
                })
            }


            // 选择背景
            const checkbox = that.m_container.querySelector('.m-container #checkbox1')
            if (checkbox) {
                checkbox.addEventListener('change', function (e) {
                    that.addLocalStore(that.bg_show_key, e.target.checked, Boolean.name)
                    that.settingBackgroundImage()
                })
            }
            // 是否关闭菜单
            const menu = that.m_container.querySelector('.m-container #checkbox2')
            if (menu) {
                menu.addEventListener('change', function (e) {
                    that.getLeftMenu(e.target.checked)
                })
            }




        }

        /**
         * 右侧操作按钮
         * @param text 指定按钮文本,默认是小虎牙或者是小鱼丸
         */
        createButton(text) {
            let that = this
            if (!that.body) {
                return;
            }
            const btn = wd.createElement('button')
            btn.style.cursor = 'pointer'
            btn.style.position = 'fixed'
            btn.style.top = '300px'
            btn.style.right = '0px'
            btn.style.padding = '5px 10px'
            btn.style.backgroundColor = 'rgb(255, 93, 35)'
            btn.style.border = 'none'
            btn.style.borderRadius = '20px'
            btn.style.fontSize = '12px'
            btn.style.color = '#fff'
            btn.style.zIndex = 100000
            btn.textContent = text ? text : (is_huya ? '小虎牙' : '小鱼丸')
            btn.addEventListener('click', function (e) {
                if (that.m_container.style.display === 'block') {
                    that.m_container.style.display = 'none'
                } else {
                    that.m_container.style.display = 'block'
                }
            })
            btn.addEventListener('mouseenter', function () {
                btn.style.backgroundColor = 'rgba(255, 93, 35,0.6)'
            })
            btn.addEventListener('mouseleave', function () {
                btn.style.backgroundColor = 'rgba(255, 93, 35,1)'
            })
            that.body.appendChild(btn)

        }


        /**
         * 删除DOM
         * @param element 需要删除的元素
         * @param realRemove 是否真实删除,默认不删除
         */
        removeDOM(element, realRemove = false) {
            try {
                if (element) {
                    element.style.display = 'none'
                    if (realRemove) {
                        element.remove()
                    }
                }
            } catch (e) {} // 防止element没有remove方法而抛出异常
        }

        /**
         * 删除DOM
         * @param selector 选择器
         * @param realRemove 是否真实删除,默认不删除
         *
         */
        removeElement(selector, realRemove = false) {
            this.removeDOM(wd.querySelector(selector), realRemove)
        }




        /**
         * 该房间是否已改被删除
         * @param url 房间链接地址 默认 window.location.href
         */
        removeRoom(url = local_url) {
            if (!this.isRemove(url)) {
                return false
            }
            this.roomIsNeedRemove();
            return true
        }


        /**
         * 房间已被删除之后操作
         * @param url 房间链接地址 默认 window.location.href
         */
        roomAlreadyRemove() {
            this.removeDOM(this.body, true)
            this.body = null; //必须设置为空!否则无法设置新的button
            const h2 = wd.createElement('h2')
            let html = wd.querySelector('html')
            let body = wd.querySelector('body')
            if (!body) { // 如果原来的删除了,从新创建一个body存放内容
                body = wd.createElement('body')
            }
            body.style.display = 'flex'
            body.style.justifyContent = 'center'
            body.style.alignItems = 'center'
            // 获取主播名称
            let name = this.getUser(this.getRoomIdByUrl(local_url)) ? this.getUser(this.getRoomIdByUrl(
                local_url)).name : ''
            h2.textContent = `主播【${name}】已被你屏蔽`
            h2.style.fontSize = '40px'

            let title = wd.querySelector('title')
            if (!title) {
                title = wd.createElement('title')
            }
            title.textContent = `主播【${name}】已被你屏蔽`
            html.appendChild(body)
            body.appendChild(h2)
            this.removeDOM(this.m_container, true)
            this.m_container = null
            // 创建操作面板
            this.create_container()
        }

        /**
         * 判断链接是否应该被删除
         * @param href 房间链接地址 默认 window.location.href
         */
        isRemove(href) {
            return this.userIsExist(this.getRoomIdByUrl(href));
        }


        /**
         * 设置背景图
         * @param url 背景图地址 默认 是默认地址
         */
        settingBackgroundImage(url) {
            if (this.getLocalStore(this.bg_show_key, Boolean.name)) {
                if (!url) {
                    url = this.getImageUrl(url)
                }
                this.body.style.backgroundSize = "cover"
                this.body.style.backgroundRepeat = 'no-repeat '
                this.body.style.backgroundAttachment = 'fixed'
                this.body.style.backgroundImage = `url(${url})`
            } else {
                this.body.style.backgroundImage = 'none'
            }

        }

        /**
         * 获取本地图片地址
         * @param url 背景图地址 默认 是默认地址
         */
        getImageUrl(url) {
            if (!url) {
                url = wls.getItem(this.bg_key)
            }
            return this.isImageUrl(url) ? url : this.defaultBackgroundImage;
        }

        /**
         * 是否是一张图片地址
         * @param url 背景图地址 默认当前壁纸
         */
        isImageUrl(url) {
            return bg_regx.test(url)
        }

        /**
         * 通过房间名称或者id判断房间是否已经保存到本地
         * @param keywords 房间名或者id
         * @param list 本地缓存数据,默认是本地缓存用户数据
         */
        userIsExist(keywords, list = this.users) {
            return this.getUser(keywords, list) ? true : false
        }


        /**
         * 通过房间名称或者id判断房间是否已经保存到本地
         * @param keywords 房间名或者id
         * @param list 本地缓存数据,默认是本地缓存用户数据
         */
        getUser(keywords, list = this.users) {
            for (let i = 0; i < list.length; i++) {
                if ((list[i].name && list[i].name == keywords) || (list[i].roomId && list[i].roomId ==
                                                                   keywords)) {
                    return list[i]
                }
            }
            return null
        }



        /**
         * 通过房间id或者房间名删除本地缓存的数据
         * @param keywords 房间名或者id
         */
        userDelete(keywords) {
            let that = this
            that.users.forEach((item, index) => {
                if (keywords == item.name || keywords == item.roomId) {
                    that.users.splice(index, 1)
                }
            })
            that.addLocalStore(this.key, this.users)
        }


        /**
         * 添加并保存直播间
         * @param id, 房间id
         * @param name 房间名
         */
        addUser(id, name) {
            if (this.userIsExist(id) || this.userIsExist(name)) {
                alert('该房间已存在!')
                return;
            }
            const newUser = new HostUser(id, name);
            // 添加
            this.users.unshift(newUser)
            // 保存到本地
            this.addLocalStore(this.key, this.users)
            this.resetTbody(this.users)
            // 如果是当前主播需要屏蔽
            if (id == this.getRoomIdByUrl(local_url)) {
                this.roomIsNeedRemove(local_url);
            }

        }

        /**
         *  获取本地保存的直播数据
         *  @param {defaultKey}  = [存储key]
         *  @param {obj}  = [需要存储的value]
         *  @param {type}  = [要解析参数类型]
         *  @param {isparse}  = [是否需要解析]
         */
        addLocalStore(defaultKey = this.key, obj = this.users, type = Array.name, isParse = true) {
            try {
                if (type == Object.name || type == Array.name) {
                    if (isParse) {
                        window.localStorage.setItem(defaultKey, JSON.stringify(obj))
                    } else {
                        window.localStorage.setItem(defaultKey, obj)
                    }
                }

                if (type == String.name || type == Boolean.name) {
                    window.localStorage.setItem(defaultKey, obj)
                }
            } catch (e) {}

        }


        /**
         * 获取本地保存的直播数据
         *  @param {key}  = [存储key]
         *  @param {type}  = [要解析参数类型]
         *  @param {isparse}  = [是否需要解析]
         */
        getLocalStore(k = this.key, type = Array.name, isParse = true) {
            let obj = window.localStorage.getItem(k)
            if (type == Array.name) {
                if (isParse) {
                    try {
                        if (obj) {
                            obj = JSON.parse(obj)
                        } else {
                            obj = []
                        }

                    } catch (e) {
                        obj = []
                    }

                }
                return Array.isArray(obj) ? obj : []
            }

            if (type == Object.name) {
                if (isParse) {
                    try {
                        if (obj) {
                            obj = JSON.parse(obj)
                        } else {
                            obj = {}
                        }

                    } catch (e) {
                        obj = {}
                    }

                }
                return obj ? obj : {}
            }

            if (type == String.name) {
                return obj ? obj : '';
            }

            if (type == Boolean.name) {
                return (obj == 'true' || obj == true) ? true : false;
            }

            return obj;

        }


        /**
         * @param {selector}  = 选择器
         * @param {selector}  = [是否真的删除,默认删除而不是display = 'none']
         * @param {time1} 循环执行时间 默认5000ms
         */
        removeVideo(selector, realyRemove = true, time1 = 5000) {
            // 第一次执行该操作
            try {
                const video = wd.querySelector(selector)
                if (video) {
                    video.pause()
                }
                this.removeDOM(video, realyRemove)
            } catch (e) {}
            // 循环执行该操作
            setInterval(() => {
                try {
                    const video = wd.querySelector(selector)
                    if (video) {
                        video.pause()
                    }
                    this.removeDOM(video, realyRemove)
                } catch (e) {}
            }, time1)
        }


        /**
         * @param {selector}  = [选择器]
         * @param {selector}  = [是否真的删除,默认删除而不是display = 'none']
         */
        roomIsNeedRemove(selector = wd.querySelector('video'), realyRemove = true) {
            // 移除直播间视频
            this.removeVideo(selector, true)
            // 添加直播间删除禁言提示
            this.roomAlreadyRemove()
            // 重新设置背景图
            this.settingBackgroundImage()
        }

        /*
         * 操作左侧导航栏,需要传入选择器,和修改值 建议放到公共方法下执行!
         * @param {selector}  = [选择器]
         * @param {value}  = [要修改的值]
         */
        getLeftMenu(value = false) {
            if (!this.menu) {
                return alert('获取不到导航菜单,操作失败!')
            }
            if (value) {
                this.menu.style.display = 'block';
            } else {
                this.menu.style.display = 'none'
            }
            this.addLocalStore(this.menu_show_key, value, Boolean.name, false)

        }

        /*
         * 操作左侧导航栏,需要传入选择器,和修改值 建议放到公共方法下执行!
         * @param {selector}  = [选择器]
         */
        loadLeftMenu() {
            //首次加载是否显示
            let d_show = this.getLocalStore(this.menu_show_key, Boolean.name, false)
            if (this.menu) {
                if (d_show) {
                    this.menu.style.display = 'block';
                } else {
                    this.menu.style.display = 'none';
                }
            }
        }

    }

    /**
     * 虎牙直播插件
     */
    class TriggerLive extends LivePlugin {
        constructor() {
            super()
            this.key = 'huyazhibo'
            this.bg_key = 'huyazhibo_bg'
            this.bg_show_key = 'huyazhibo_bg_show'
            this.menu_show_key = 'huyazhibo_menu_show_key'
            this.defaultBackgroundImage = 'https://livewebbs2.msstatic.com/huya_1664197944_content.jpg'
            this.baseUrl = "https://www.huya.com/"
            this.users = this.getLocalStore(this.key, Array.name, true)
            this.html = wd.querySelector('html')
            this.body = wd.querySelector('body')
            this.menu = wd.querySelector('.mod-sidebar')
            this.tbody = null
            this.m_container = null
            // 初始化,请务必调用该方法!!!
            this.init()
        }


        // 首页操作
        index() {
            // 直播源
            const url = local_url
            if (url == this.baseUrl) {
                // 操作视频
                this.removeVideo('.mod-index-main video', true)
                // 触发点击关闭广告
                const banner_close = wd.querySelector('.mod-index-wrap #banner i')
                if (banner_close) {
                    banner_close.click();
                }

            }

        }
        // 分类页操作
        category() {
            if (new RegExp(/^https:\/\/.*\.huya\.((com)|(cn))\/g(\/.*)$/).test(local_url)) {
                let that = this
                const dd = wd.querySelectorAll('.live-list-nav dd')
                if (dd) {
                    for (let d of dd) {
                        d.addEventListener('click', () => {
                            setTimeout(() => {
                                that.removeRoomByClickRoomName()
                            }, 2000)

                        })

                    }
                }
            }
        }
        // 公共部分操作
        common() {
            this.removeRoomByClickRoomName()
        }
        // 详情操作
        detail() {
            if (new RegExp(/^https:\/\/www\.huya\.com(\/\w+)$/).test(local_url)) {
                let that = this
                // 点击直播间移除直播间操作
                const hostName = wd.querySelector('.host-name')
                if (hostName) {
                    hostName.addEventListener('click', () => {
                        if (confirm(`确认禁用 ${hostName.textContent}?`)) {
                            that.addUser(that.getRoomIdByUrl(local_url), hostName.textContent)
                        }
                    })

                }
            }
        }
        // 通过地址获取房间号
        getRoomIdByUrl = (url = local_url) => {
            let arr = url.split('/')
            let roomId = arr[arr.length - 1]
            return roomId
        }

        // 通过房间号查找名称
        getNameByRoomId(roomId) {
            let that = this
            const hostName = document.querySelector('.host-name')
            if (!hostName) {
                const rooms = document.querySelectorAll('.game-live-item')
                if (rooms) {
                    for (let room of rooms) {
                        const a = room.querySelector('a')
                        if (a && a.href) {
                            const id = that.getRoomIdByUrl(a.href)
                            const user = room.querySelector('.txt i')
                            if (id === roomId) {
                                hostName = user
                            }
                        }

                    }
                }
            }
            return hostName && hostName.textContent ? hostName.textContent : ''
        }

        // 通过点击直播间名称删除直播间
        removeRoomByClickRoomName() {
            const that = this
            const rooms = document.querySelectorAll('.game-live-item')
            if (rooms) {
                for (let li of rooms) {
                    try {
                        const a = li.querySelector('a')
                        // 获取单个主播间房间地址
                        const url = a.href
                        // 获取房间i
                        const user = li.querySelector('.txt i')
                        const name = user.textContent || ''
                        user.addEventListener('click', () => {
                            if (confirm(`确认禁用 ${name}?`)) {
                                that.addUser(that.getRoomIdByUrl(url), name);
                                that.removeDOM(li);
                            }
                        })
                        if (that.isRemove(url)) {
                            that.removeDOM(li)
                        }
                    } catch (e) {}

                }
            }

        }

    }

    /**
     * 斗鱼直播插件
     */
    class FishLive extends LivePlugin {
        constructor() {
            super()
            this.key = 'douyuzhibo'
            this.bg_key = 'douyuzhibo_bg'
            this.bg_show_key = 'douyuzhibo_show'
            this.menu_show_key = 'douyuzhibo_menu_show_key'
            this.baseUrl = "https://www.douyu.com/"
            this.defaultBackgroundImage =
                'https://sta-op.douyucdn.cn/dylamr/2022/11/07/1e10382d9a430b4a04245e5427e892c8.jpg'
            this.users = this.getLocalStore(this.key, Array.name, true)
            this.html = wd.querySelector('html')
            this.body = wd.querySelector('body')
            this.menu = wd.querySelector('#js-aside')
            this.tbody = null
            this.m_container = null
            // 初始化,请务必调用该方法!!!
            this.init()
        }


        // 公共部分页面操作
        common() {}
        //首页操作
        index() {
            let that = this
            // 直播源
            if (window.location.href == that.baseUrl) {
                window.scroll(0, 0)
                // 移除直播
                that.removeVideo('.layout-Slide-player video', true)
                // 获取暂停button
                const vbox = wd.querySelector('#room-html5-player');
                const divs = vbox.querySelectorAll('div')
                if (divs) {
                    divs.forEach(div => {
                        if (div && div.title && div.title == '暂停') {
                            div.click()
                        }
                    })

                }
                // 初始化默认高度,默认浏览器可视化高度
                let init = window.innerHeight;
                // 初始化容器存放用户
                let init_users = []
                // 页面加载完毕
                setTimeout(() => {
                    that.removeRoomByClickRoomName(init_users)
                }, time)
                // 斗鱼直播使用懒加载方式,只有鼠标下滑,下方直播间才会加载,首次加载不会加载所有页面直播间列表
                // 因此,添加滚动事件来添加
                // 另外防止二次或者多次添加点击事件,将之前保存到init_users中来记录是否该添加
                window.addEventListener('scroll', (e) => {
                    // 超过可视化高度,需要重新加载
                    if (window.pageYOffset > init) {
                        init = init + 100;
                        // 重新扫描点击事件
                        that.removeRoomByClickRoomName(init_users)
                    }

                })

            }
        }
        // 分类页面操作
        category() {
            let that = this
            // 匹配分类页
            if (new RegExp(/https:\/\/www.douyu.com(\/((directory.*)|(g_.*)))$/).test(window.location.href)) {
                that.removeRoomByClickRoomName()
                const labels = wd.querySelectorAll('.layout-Module-filter .layout-Module-label')
                if (labels) {
                    for (let label of labels) {
                        if (label) {
                            label.addEventListener('click', (e) => {
                                e.preventDefault()
                                // 获取当前地址
                                let to_link = label && label.href ? label.href : null
                                if (to_link) {
                                    window.location.href = to_link
                                } else {
                                    // 获取全部地址
                                    var result = 'https://www.douyu.com/g_' + local_url.match(RegExp(
                                        /subCate\/.*/g))[0].replace('subCate', '').match(new RegExp(
                                        /\w+/g))[0]
                                    window.location.href = result
                                }

                            })

                        }

                    }
                }

            }


        }
        // 详情页操作
        detail() {
            let that = this
            window.scroll(0, 0)
            // 匹配只有在播放直播间才会生效
            if (new RegExp(/.*douyu.*(\/((.*rid=\d+)|(\d+)))$/).test(local_url)) {
                // 详情页名称操作
                setTimeout(() => {
                    // 点击主播直播间名称进行操作
                    const hostName = wd.querySelector('.Title-roomInfo h2.Title-anchorNameH2')
                    if (hostName) {
                        hostName.addEventListener('click', () => {
                            if (confirm(`确认禁用 ${hostName.textContent}?`)) {
                                that.addUser(that.getRoomIdByUrl(local_url), hostName
                                             .textContent)
                            }
                        })

                    }

                    // 带有轮播图 广告
                    if (new RegExp(/.*douyu.*\/topic(\/(.*rid=\d+))$/).test(local_url)) {


                    }

                    // 不带有轮播图 广告
                    if (new RegExp(/.*douyu.*(\/(\d+))$/).test(local_url)) {
                        // 如果是小窗口
                        setTimeout(() => {
                            const closeBtn = document.querySelector('.roomSmallPlayerFloatLayout-closeBtn')
                            if (closeBtn) {
                                closeBtn.click()
                            }
                        }, time)

                    }
                }, time)

            }

        }
        // 通过点击直播间名称删除直播间
        removeRoomByClickRoomName(list = []) {
            let that = this
            if (this.baseUrl == local_url) {
                const room = wd.querySelectorAll('.layout-Wrapper.layout-Module.RoomList .layout-List-item')
                if (room) {
                    for (let li of room) {
                        try {
                            // 获取单个主播间房间地址
                            const a = li.querySelector('a')
                            if (a) {
                                a.onclick = (e) => {
                                    e.preventDefault()
                                }
                                const url = a.href
                                const user = li.querySelector('.DyCover-user')
                                const name = user.textContent || ''
                                if (user && (!that.userIsExist(name, list) || !that.userIsExist(
                                    url, list))) {
                                    // 添加记录,下次不再添加!!!
                                    list.unshift(new HostUser(url, name))
                                    user.addEventListener('click', () => {
                                        if (confirm(`确认禁用 ${name}`)) {
                                            that.addUser(that.getRoomIdByUrl(url), name);
                                            that.removeDOM(li);
                                        }
                                    }, true)
                                }

                                if (that.isRemove(url) || that.userIsExist(name)) {
                                    that.removeDOM(li)
                                }

                            }

                        } catch (e) {}

                    }
                }
            }

            if (new RegExp(/https:\/\/www.douyu.com(\/((directory.*)|(g_.*)))$/).test(local_url)) {
                const rooms = wd.querySelectorAll('.layout-Cover-item')
                if (rooms) {
                    for (let li of rooms) {
                        try {
                            if (li) {
                                const link = li.querySelector('a.DyListCover-wrap')
                                if (link) {
                                    link.addEventListener('click', (e) => {
                                        e.preventDefault()
                                    })
                                    const url = link.href
                                    const user = link.querySelector('div.DyListCover-userName')
                                    const name = user.textContent || ''

                                    // 判断该直播间列表窗口是否需要删除
                                    if (that.isRemove(url) || that.userIsExist(name)) {
                                        that.removeDOM(li, true)
                                    } else {
                                        if (user) {
                                            user.addEventListener('click', (e) => {
                                                if (confirm(`确认禁用 ${name}?`)) {
                                                    const id = that.getRoomIdByUrl(url);
                                                    that.addUser(id, name);
                                                    that.removeDOM(li);
                                                }
                                                e.preventDefault()
                                            })
                                        }


                                        // 监听鼠标移入事件
                                        li.addEventListener('mouseenter', (e) => {
                                            e.preventDefault()
                                            const a = e.target.querySelector(
                                                'a.DyListCover-wrap.is-hover')
                                            if (a) {
                                                const url = a.href
                                                const user = a.querySelector('.DyListCover-userName')
                                                const name = user.textContent || ''
                                                if (user) {
                                                    user.addEventListener('click', (a) => {
                                                        a.preventDefault()
                                                        if (confirm(`确认禁用 ${name}?`)) {
                                                            const id = that.getRoomIdByUrl(url);
                                                            that.addUser(id, name);
                                                            that.removeDOM(li);
                                                        }

                                                    })
                                                }

                                                a.addEventListener('click', (t) => {
                                                    t.preventDefault()
                                                })

                                            }


                                        })
                                    }

                                }
                            }

                        } catch (e) {}
                    }
                }

            }

        }
        // 通过房间号获取直播间name
        getNameByRoomId(keywords) {
            let that = this
            // 从详情页获取
            const hostName = document.querySelector('.Title-blockInline .Title-anchorName h2')
            let rooms = null;
            if (!hostName) {
                rooms = document.querySelectorAll('.layout-List-item')
                // index
                if (rooms) {
                    for (let room of rooms) {
                        const id = that.getRoomIdByUrl(room.querySelector('a').href)
                        const user = room.querySelector('.DyCover-user')
                        if (id == keywords) {
                            hostName = user
                        }
                    }
                }
                // 如果还是获取不到从分类页面获取
                if (!hostName) {
                    rooms = document.querySelectorAll('.layout-Cover-item')
                    if (rooms) {
                        for (let room of rooms) {
                            const id = that.getRoomIdByUrl(room.querySelector('a').href)
                            const user = room.querySelector('.DyListCover-userName')
                            if (id == keywords) {
                                hostName = user
                            }
                        }
                    }
                }


            }
            return hostName && hostName.textContent ? hostName.textContent : ''
        }
        // 通过房间地址获取房间号
        getRoomIdByUrl(url) {
            try {
                if (new RegExp(/https:\/\/.*(rid=.*)$/).test(local_url)) {
                    return local_url.match(new RegExp(/rid=.*/g))[0].replace('rid=', '')
                } else {
                    let arr = url.split('/')
                    let roomId = arr[arr.length - 1]
                    return roomId
                }

            } catch (e) {
                return null
            }
        }

    }


    // 样式部分
    GM_addStyle(`
	.m-container {
	     box-sizing: border-box;
	     position: fixed;
	     display: none;
	     width: 600px;
	     height: 400px;
	     top: 100px;
	     left: 50%;
	     border-radius: 0;
	     overflow: hidden scroll;
	     background-color: white;
	     transform: translateX(-50%);
	     z-index:1000;
	     transition: display linear 1s;
	     box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2),
	     -2px -2px 2px rgba(0, 0, 0, 0.2);
	   }
	   .m-container .operation {
	     box-sizing: border-box;
	     height: 80px;
	     padding: 20px 0 0 0;
	     text-align: center;
	   }
	    .m-container .operation input[type="text"] {
	     width:130px;
	     box-sizing: border-box;
	     outline: none;
	     border: 1px solid teal;
	     padding: 5px;
	   }
	   .m-container .operation input[type="text"]:focus {
	     border: 2px solid teal !important;
	   }
	   .m-container .operation input[type="checkbox"] {
	     display:inline !important;
	   }
        .m-container .operation input[type="file"] {
	     display:none !important;
	   }
	   .m-container table {
	     position: relative;
	     box-sizing: border-box;
	     overflow: hidden;
	     padding: 10px;
	     text-align: left !important;
	     margin: 0 auto;
	     max-height:200px;
	     width: 90%;
	   }
	   .m-container  table tbody {
	     max-height: 250px;
	     text-align: left !important;
	   }
	   .m-container table thead{
	     border-top: 1px solid rgba(0,0,0,0.4);
	     border-bottom: 1px solid rgba(0,0,0,0.4);
	     text-align: left !important;
	     padding: 10px;
	   }
	   .m-container table th, m-container table td {
	     padding: 10px;
	   }
	   .m-container table tr {
	     border-bottom: 1px solid rgba(0,0,0,0.4);
	     margin:5px 0;
	   }
	   .m-container .m-link,.m-container .m-link:visited{
	      color:blnk !important;
	   }
	   .m-container .m-link:hover{
	      color:blue !important;
	      text-decoration:underline !important;
	   }
	   .m-container .btn {
	     cursor: pointer !important;
	     padding: 5px 7px !important;
	     border: none !important;
	     color: #fff !important;
	     font-size:10px !important;
	     border-radius:20px !important;
	     max-width:50px  !important;
	     margin:0 0 !important;
         background-color:rgba(166, 169, 173,1) !important;
	     z-index:1000 !important;
	   }
        .m-container .btn:hover {
           background-color:rgba(166, 169, 173,0.6) !important;
	   }
	   .m-container .btn-teal{
	     background-color:rgba(64, 158, 255,1)  !important;
	   }
	  .m-container .btn-teal:hover{
	     background-color:rgba(64, 158, 255,0.6) !important;
	   }
	   .m-container .btn-success{
	     background-color: rgba(103, 194, 58,1) !important;
	   }
	    .m-container .btn-success:hover{
	     background-color: rgba(103, 194, 58,0.6) !important;
	   }
	   .m-container .btn-info{
	     background-color:rgba(119, 119, 119,1) !important;
	   }
	   .m-container .btn-info:hover{
	      background-color:rgba(119, 119, 119,0.6) !important;
	   }
       .m-container .btn-warning{
	     background-color:rgba(230, 162, 60,1) !important;
	   }
	   .m-container .btn-warning:hover{
	      background-color:rgba(230, 162, 60,0.6) !important;
	   }
	   .m-container .btn-danger{
	     background-color:rgba(245, 108, 108,1) !important;
	   }
	    .m-container .btn-danger:hover{
	     background-color:rgba(245, 108, 108,0.6) !important;
	   }
	   .game-live-item i,.host-name {
	       cursor:pointer;
	   }
	   .game-live-item .txt i:hover,.host-name:hover {
	       color:rgb(255, 135, 0);
	   }
	   .layout-List-item .DyCover-content .DyCover-user,.layout-Cover-item .DyListCover-userName,.Title-blockInline .Title-anchorName h2{
	       cursor:pointer !important;
	   }
	   .layout-List-item .DyCover-content .DyCover-user:hover,.layout-Cover-item .DyListCover-userName:hover,.Title-blockInline .Title-anchorName h2:hover {
	       color:rgb(255, 135, 0) !important;
        }
        /********************斗鱼直播********************************/
      .layout-Section.layout-Slide .layout-Slide-player,

      .layout-Slide-bannerInner,
       #lazyModule3,
       #lazyModule4,
       #lazyModule5,
       #lazyModule6,
       #lazyModule7,
       #lazyModule8,
       #lazyModule23,
       #lazyModule24,
       #js-room-activity,
       #js-right-nav,
       #js-bottom,
       #js-header .Header .HeaderNav,
       #js-header .Header .HeaderGif-left,
       #js-header .Header .HeaderGif-right,
       .Header-download-wrap,
       .AnchorInterToolsUser,
       #js-room-activity,
       #js-right-nav,
       #js-bottom,
       li.Header-menu-link,
       .layout-Main .layout-Customize,
       .HeaderCell-label-wrap,
       .Title-AnchorLevel,.RoomVipSysTitle,
       .Aside-nav .Aside-nav-item,
       .Title-roomInfo .Title-row,
       .multiBitRate-da4b60{
           display:none !important;
       }


        li.Header-menu-link:nth-child(1),
        li.Header-menu-link:nth-child(2),
        li.Header-menu-link:nth-child(3),
        .Aside-nav .Aside-nav-item:nth-child(1),
        .Title-roomInfo .Title-row:nth-child(1),
		.Title-roomInfo .Title-row:nth-child(2) {
           display:inline-block !important;
       }
       .Barrage-main  .UserLevel,
       .Barrage-main  .js-user-level,
       .Barrage-main  .Barrage-icon,
       .Barrage-main  .Motor,
       .Barrage-main  .Motor-flag,
       .Barrage-main  .Barrage-hiIcon,
       .Barrage-main  .UserGameDataMedal,
       .Barrage-main  .ChatAchievement,
       .Barrage-main  .Barrage-notice,
       .layout-Player .layout-Player-announce,
       .layout-Player .layout-Player-rank,
        #js-player-toolbar,
       .MatchSystemTeamMedal,
       .Barrage .Barrage-userEnter{
         display:none !important;
       }
	   #root div.layout-Main{
		   margin-top:70px !important;
	   }

       div#root div.wm-general {
             display: none !important;
             background: none !important;
       }

       div#root div.wm-general:nth-child(3) {
             display: inline-block !important;
       }
         div#bg3{
          background:none !important;
        }

       .Barrage-main .Barrage-content {
        color:#333 !important;
       }
       .Barrage-main .Barrage-nickName{
        color:#2b94ff !important;
       }

       .Barrage-listItem{
         color: #333 !important;
         background-color: #f2f5f6 !important;
       }

       .layout-Player-barrage{
           position: absolute !important;
           top: 0 !important;
        }

       /********************虎牙直播********************************/
       .helperbar-root--12hgWk_4zOxrdJ73vtf1YI,
       .mod-index-wrap .mod-index-main .main-bd,
       .mod-index-wrap .mod-index-main .main-hd,
       .mod-index-wrap #js-main,
       .mod-index-wrap #banner,
       .mod-index-wrap .mod-game-type,
       .mod-index-wrap .mod-actlist,
       .mod-index-wrap .mod-news-section,
       .mod-index-wrap .mod-index-list .live-box #J_adBnM,
       .mod-index-wrap .mod-index-recommend,
       .mod-index-wrap .mod-news-section,
       .mod-index-wrap .recommend-wrap,
       .liveList-header-r,
       .room-footer,
       .J_roomSideHd,
        #J_roomSideHd,
        #player-gift-wrap,
        #match-cms-content,
        #matchComponent2,
       .hy-nav-item,
       .list-adx,
       .layout-Banner,
       .room-weeklyRankList{
           display:none !important;
        }
        .hy-nav-item:nth-child(1),
        .hy-nav-item:nth-child(2),
        .hy-nav-item:nth-child(3)
        {
          display:inline-block !important;
        }
        .mod-index-wrap .mod-index-list{
          margin-top:80px !important;
        }
        .duya-header{
          background: hsla(0,0%,100%,.95)  !important;
          border-bottom: 1px solid #e2e2e2 !important;
          box-shadow: 0 0 6px rgb(0 0 0 / 6%) !important;
        }
        .duya-header a,.duya-header i{
         color:#000 !important;
        }
       .chat-room__list .msg-normal,.chat-room__list .msg-bubble{
          background:none !important;
        }
       .chat-room__list .msg-normal-decorationPrefix,
       .chat-room__list .msg-normal-decorationSuffix,
       .chat-room__list .msg-bubble-decorationPrefix,
       .chat-room__list img,
       .chat-room__list .msg-noble,
       .chat-room__list .msg-sys,
       .chat-room__list .msg-auditorSys,
       .J_box_msgOfKing,
       .chat-room__list .msg-onTVLottery{
           display: none;
        }
       .chat-room__list .msg-bubble span.msg {
           color: #333 !important:
           background:none!important:
         }
       .chat-room__list .msg-bubble .colon,
       .chat-room__list .msg-bubble .msg,
       .chat-room__list .name
        {
           color #3c9cfe !important:
           background:none!important:
         }

 `)

})()