Greasy Fork is available in English.

[Bilibili] 记住关注视频(FoMan插件)

记住你是因为哪个视频点的关注(FoMan的插件)

安装此脚本?
作者推荐脚本

您可能也喜欢[Bilibili] 关注管理器

安装此脚本
// ==UserScript==
// @name         [Bilibili] 记住关注视频(FoMan插件)
// @namespace    ckylin-script-bili-foman-plugins-remeber-follows
// @version      0.1
// @description  记住你是因为哪个视频点的关注(FoMan的插件)
// @author       CKylinMC
// @match        https://www.bilibili.com/video/*
// @match        https://space.bilibili.com/*
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @license      GPLv3
// ==/UserScript==

(function() {
    'use strict';
    const wait = ms=>new Promise(r=>setTimeout(r,ms));
    const get = (q,p=document)=>p.querySelector(q);
    // const getAll = (q,p=document)=>[...p.querySelectorAll(q)];
    const getAPI = (bvid) => fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bvid).then(raw => raw.json());
    // const getAidAPI = (aid) => fetch('https://api.bilibili.com/x/web-interface/view?aid=' + aid).then(raw => raw.json());
    const waitForDom = async (query,domparent=document,maxRetries=20,gagms=200)=>{
        let i = maxRetries;
        while(--i>0){
            if(get(query,domparent)) return true;
            await wait(gagms);
        }
        return false;
    };
    // const waitForAttribute = async (q, attr)=>{
    //     let i = 50;
    //     let value;
    //     while (--i >= 0) {
    //         if ((attr in q) &&
    //             q[attr] != null) {
    //             value = q[attr];
    //             break;
    //         }
    //         await wait(100);
    //     }
    //     return value;
    // }
    const cfg={
        followbtn:"#v_upinfo .follow-btn.b-gz",
        unfollowbtn: "ul.follow_dropdown"
    };
    /*
    persist
    "mid":[
        [0]: timestamp,
        [1]: string videoName (at that time),
        [2]: string videoID,
        [3]: string upName (at that time)
    ]
    */
    class FollowTrackManager{
        async followed(){
            const res = await getAPI(unsafeWindow.bvid);
            if(!res.code===0)return false;
            if(!res.data) return false;
            const timestamp = new Date().getTime();
            const videoId = unsafeWindow.bvid||res.data.bvid;
            const videoName = res.data.title||"";
            const upName = res.data.owner.name||"";
            const mid = res.data.owner.mid||0;
            GM_setValue(mid,[timestamp,videoName,videoId,upName]);
            console.log('FoMan: Remembered for this UP.');
        }

        async unfollowed(){
            const res = await getAPI(unsafeWindow.bvid);
            if(!res.code===0)return false;
            if(!res.data) return false;
            const mid = res.data.owner.mid||0;
            this.unfollow(mid);
        }

        async unfollow(mid){
            GM_setValue(mid,null);
            console.log('FoMan: Removed record for this UP.');
        }

        get(mid){
            const info = GM_getValue(mid);
            if(!info) return null;
            let [timestamp,videoName,videoId,upName] = info;
            return {timestamp,videoName,videoId,upName,mid};
        }
    }
    const man = new FollowTrackManager;

    async function onFollow(){
        console.debug("FoMan: triggered new following callback");
        man.followed();
        addUnfoListener();
    }

    async function onUnfollow(){
        console.debug("FoMan: triggered unfollowing callback");
        man.unfollowed();
    }

    async function clickHandler(e){
        const btn = get(cfg.followbtn);
        if(!btn) return;
        if(btn.classList.contains("not-follow")) return onFollow();
        else if(e.target.innerText.indexOf("取消关注")>-1) return onUnfollow();
        else console.warning("FoMan: 这啥?",e);
    }

    async function addUnfoListener(add=true){
        await waitForDom(cfg.unfollowbtn);
        const btn = get(cfg.unfollowbtn);
        if(!btn) return;
        btn.removeEventListener("mouseup",clickHandler);
        add&&btn.addEventListener("mouseup",clickHandler);
    }

    async function decideAddUnfoListener(){
        const btn = get(cfg.followbtn);
        if(!btn) return;
        if(btn.classList.contains("following")) {
            return addUnfoListener();
        }
    }

    async function registerEventsListener(){
        await waitForDom(cfg.followbtn);
        const btn = get(cfg.followbtn);
        btn.removeEventListener("mouseup",clickHandler);
        btn.addEventListener("mouseup",clickHandler);
        btn.removeEventListener("mouseenter",decideAddUnfoListener);
        btn.addEventListener("mouseenter",decideAddUnfoListener);
    }
    if(location.pathname.startsWith("/video/")){
        registerEventsListener();
    }
    if(!unsafeWindow.FoManPlugins){
        unsafeWindow.FoManPlugins = {}
    }
    unsafeWindow.FoManPlugins.RememberFollows = man
})();