B站直播增强型关注列表 Beta

大点儿操作方便

目前为 2021-05-07 提交的版本。查看 最新版本

// ==UserScript==
// @name         B站直播增强型关注列表 Beta
// @namespace    http://tampermonkey.net/
// @version      1.1.4
// @description  大点儿操作方便
// @author       SoraYuki & TGSAN
// @include      /https:\/\/live.bilibili.com\/.*/
// @grant        none
// @noframes
// ==/UserScript==

(function () {
    'use strict';

    let isHomePage = document.location.pathname == "/";
    let hasInit = false; // 插件是否初始化成功
    let followListOpened = false; // 关注列表开启状态
    let followListLoading = false; // 列表是否仍在拉取

    function secondsToHms(seconds) {
        let SECONDS_PER_DAY = 86400;
        let HOURS_PER_DAY = 24;
        let days = Math.floor(seconds / SECONDS_PER_DAY);
        let remainderSeconds = seconds % SECONDS_PER_DAY;
        let hms = new Date(remainderSeconds * 1000).toISOString().substring(11, 19);
        return hms.replace(/^(\d+)/, h => `${Number(h) + days * HOURS_PER_DAY}`.padStart(2, '0'));
    };

    function createLiveCard(avatarUrl, userName, titleName, link, verifyNum, tiptext) {
        let getVerifyColor = function (_verifyNum) {
            switch (_verifyNum) {
                case -1:
                    return "#fd5d91ff"; // 普通用户
                case 0:
                    return "#feb959ff"; // 个人
                case 1:
                    return "#50c8fdff"; // 企业
                default:
                    return "#000000ff";
            }
        };
        let textStyle = "line-height: 1.15; margin: 10px 0; display: block; overflow: hidden; -o-text-overflow: ellipsis; text-overflow: ellipsis; white-space: nowrap; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;";
        let avatarStyle = "background-image: url("" + avatarUrl + ""); vertical-align: top; width: 40px; height: 40px; margin-top: 10px; display: inline-block; border-radius: 50%; background-color: #F7F7F7; background-size: cover; box-shadow: 0 0 10px 1px " + getVerifyColor(verifyNum) + ";";
        let cardRoot = document.createElement("div");
        cardRoot.style = "width: calc(100% - 40px); height: max-content; margin: 10px 10px; padding: 0px 10px; border-radius: 10px; background-color: #f5f5f8;";
        let innerHTML = '<a title="' + tiptext + '" href="' + link + '" target="' + (isHomePage === true ? "_blank" : "_self") + '" ' + (isHomePage === true ? 'onclick="playerInstance.pause()"' : '') + ' style="text-decoration: none; margin: 0;">';
        innerHTML += '<div style="' + avatarStyle + '"></div>';
        innerHTML += '<div style="vertical-align: top; display: inline-block; padding-left: 10px; overflow-x: hidden; -webkit-box-sizing: border-box; box-sizing: border-box; width: calc(100% - 40px);">';
        innerHTML += '<p style="font-size: 15px; color: #000; ' + textStyle + '">' + userName + '</p>';
        innerHTML += '<p style="font-size: 12px; color: #999; ' + textStyle + '">' + titleName + '</p>';
        innerHTML += '</div>';
        cardRoot.innerHTML = innerHTML;
        return cardRoot;
    }

    function clearList(followListBodyDiv) {
        while (followListBodyDiv.lastElementChild) {
            followListBodyDiv.removeChild(followListBodyDiv.lastElementChild);
        }
    }

    async function loadList(followListBodyDiv) {
        clearList(followListBodyDiv);

        // 标题
        let titleDiv = document.createElement("div");
        titleDiv.style = "width: max-content; padding: 20px 10px 10px 20px;";
        titleDiv.innerHTML = '<a href="https://link.bilibili.com/p/center/index#/user-center/follow/1" target="_blank" style="text-decoration: none;"><span style="font-size: 18px; color: #23ade6; line-height: 24px;"></span></a>';
        followListBodyDiv.appendChild(titleDiv);
        let titleText = titleDiv.children[0].children[0];
        titleText.innerText = "我的关注 - 载入中...";

        let j;
        try {
            let result = await fetch('https://api.live.bilibili.com/xlive/app-interface/v1/relation/liveAnchor', { credentials: 'include' });
            j = await result.json();
        } catch (e) {
            titleText.innerText = "我的关注 - 列表拉取失败 (゚∀。)";
            return;
        }

        let count = j.data.total_count;

        if (j.data.rooms != undefined)
        {
            try {
                j.data.rooms.forEach(room => {
                    let tiptext = "人气:" + room.online + "  已开播:" + secondsToHms((Date.now() - (room.live_time * 1000)) / 1000);
                    followListBodyDiv.appendChild(createLiveCard(room.face, room.uname, room.title, "//live.bilibili.com/" + room.roomid, room.official_verify, tiptext));
                })
            } catch (e) {
                titleText.innerText = "我的关注 - 列表读取失败 (゚∀。)";
                return;
            }
        }
        
        // 页脚
        let footerDiv = document.createElement("div");
        footerDiv.style = "height: 40px;";
        followListBodyDiv.appendChild(footerDiv);

        titleText.innerText = "我的关注 (" + count + ")";
    };

    async function loadListOld(followListBodyDiv) {
        clearList(followListBodyDiv);

        // 标题
        let titleDiv = document.createElement("div");
        titleDiv.style = "width: max-content; padding: 20px 10px 10px 20px;";
        titleDiv.innerHTML = '<a href="https://link.bilibili.com/p/center/index#/user-center/follow/1" target="_blank" style="text-decoration: none;"><span style="font-size: 18px; color: #23ade6; line-height: 24px;"></span></a>';
        followListBodyDiv.appendChild(titleDiv);
        let titleText = titleDiv.children[0].children[0];
        titleText.innerText = "我的关注 - 载入中...";

        let j;
        try {
            let result = await fetch('https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page=1&page_size=10', { credentials: 'include' });
            j = await result.json();
        } catch (e) {
            titleText.innerText = "我的关注 - 列表拉取失败 (゚∀。)";
            return;
        }

        let count = j.data.count;
        let offset = 0;
        while (count > offset) {
            try {
                if (offset > 0) {
                    let result = await fetch('https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page=' + (offset / 10 + 1) + '&page_size=10', { credentials: 'include' });
                    j = await result.json();
                }
            } catch (e) {
                titleText.innerText = "我的关注 - 列表拉取失败 (゚∀。)";
                return;
            }

            for (let i = 0; i < j.data.rooms.length; ++i) {
                let x = j.data.rooms[i];
                followListBodyDiv.appendChild(createLiveCard(x.face, x.uname, x.title, x.link, ""));
            }
            offset += j.data.rooms.length;
        }
        // 页脚
        let footerDiv = document.createElement("div");
        footerDiv.style = "height: 40px;";
        followListBodyDiv.appendChild(footerDiv);

        titleText.innerText = "我的关注 (" + count + ")";
    };

    function openList(followListFrameDiv, followListBodyDiv) {
        if (followListOpened === false) {
            followListOpened = true;
            followListFrameDiv.style.setProperty("width", "320px");
            followListFrameDiv.style.setProperty("opacity", "1");
            followListFrameDiv.style.setProperty("pointer-events", "auto");
            if (followListLoading === false) {
                followListLoading = true;
                loadList(followListBodyDiv).then(function () {
                    followListLoading = false;
                    if (followListOpened === false) {
                        clearList(followListBodyDiv);
                    }
                });
            }
        }
    }

    function closeList(followListFrameDiv, followListBodyDiv) {
        if (followListOpened === true) {
            followListOpened = false;
            followListFrameDiv.style.setProperty("width", "0px");
            followListFrameDiv.style.setProperty("opacity", "0");
            followListFrameDiv.style.setProperty("pointer-events", "none");
            clearList(followListBodyDiv);
        }
    }

    let initIntervalId = setInterval(function () {
        let oldFollowBtn = document.querySelector(".side-bar-btn[data-upgrade-intro='Follow']"); // 直播间
        if (oldFollowBtn == null) {
            oldFollowBtn = document.querySelector(".sidebar-btn[title='关注']"); // 首页
        }
        // if (oldFollowBtn == null) {
        //     if (document.querySelector(".sidebar-btn[title='直播间']") != null) { // 有直播间选项视为登录(不可用)状态
        //         oldFollowBtn = document.querySelector(".sidebar-btn[title='推荐']"); // 首页(新版)
        //     }
        // }
        if (oldFollowBtn != null) {
            hasInit = true;
            let newFollowBtn = oldFollowBtn.cloneNode(true);
            oldFollowBtn.parentNode.replaceChild(newFollowBtn, oldFollowBtn);
            newFollowBtn.addEventListener("click", function (event) {
                event.cancelBubble = true; // 重新阻止按钮点击事件传递
            });
            let followListFrameDiv = document.createElement("div");
            followListFrameDiv.id = "plugin-follow-list-div";
            followListFrameDiv.style.setProperty("position", "fixed");
            followListFrameDiv.style.setProperty("height", "100vh");
            followListFrameDiv.style.setProperty("width", "0px");
            followListFrameDiv.style.setProperty("background-color", "rgba(255, 255, 255, 1)");
            followListFrameDiv.style.setProperty("opacity", "0");
            followListFrameDiv.style.setProperty("z-index", "9999");
            followListFrameDiv.style.setProperty("top", "0");
            followListFrameDiv.style.setProperty("left", "0");
            followListFrameDiv.style.setProperty("box-shadow", "rgba(0, 0, 0, 0.22) 1px 0px 12px 0px");
            followListFrameDiv.style.setProperty("transition", "opacity .4s cubic-bezier(.22,.58,.12,.98), width .4s cubic-bezier(.22,.58,.12,.98)");
            followListFrameDiv.style.setProperty("overflow-y", "auto");
            followListFrameDiv.style.setProperty("pointer-events", "none");
            followListFrameDiv.addEventListener("click", function (event) {
                event.cancelBubble = true; // 阻止关注列表点击事件传到父级
            });
            document.addEventListener("click", function () {
                closeList(followListFrameDiv, followListBodyDiv); // 点其他元素(会传递事件的)收回列表
            });
            document.body.appendChild(followListFrameDiv);
            let followListBodyDiv = document.createElement("div");
            followListBodyDiv.style.setProperty("height", "100%");
            followListBodyDiv.style.setProperty("width", "100%");
            followListFrameDiv.appendChild(followListBodyDiv);
            newFollowBtn.addEventListener('click', function () {
                if (followListOpened === true) {
                    closeList(followListFrameDiv, followListBodyDiv);
                } else {
                    openList(followListFrameDiv, followListBodyDiv);
                }
            });
            let oldDiag = document.querySelector(".follow-cntr");
            if (oldDiag != null) {
                oldDiag.parentElement.hidden = true;
            }
            clearInterval(initIntervalId);
        }
    }, 100);

    setTimeout(function () {
        if (hasInit === false) {
            clearInterval(initIntervalId);
        }
    }, 5000); // 元素查找超时(5s)
})();

QingJ © 2025

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