bilibili 显示 av 号

在视频播放页面显示视频的 av 号

// ==UserScript==
// @name         bilibili 显示 av 号
// @namespace    https://github.com/8qwe24657913
// @version      0.3
// @description  在视频播放页面显示视频的 av 号
// @author       8q
// @match        http://www.bilibili.com/*
// @match        https://www.bilibili.com/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

// eslint-disable-next-line no-extra-semi
;(function () {
    'use strict'
    let aid, link, anchor
    const setData = () => {
        link.href = `//www.bilibili.com/video/av${aid}/`
        link.textContent = `av${aid}`
    }
    const run = () => {
        // aid 应为全数字
        if (!/^\d+$/.test(aid)) {
            console.error('bilibili 显示 av 号: aid 格式错误', 'aid=', aid)
            return
        }
        // 已插入链接且未被 vue 删除,常见于通过 pushState + ajax 实现的无刷新跳页
        if (link && anchor && document.contains(anchor)) {
            setData()
            return
        }
        anchor = document.querySelector('.video-data span:not([title]):not([class]), .pub-info')
        if (!anchor) {
            console.error('bilibili 显示 av 号: 未能找到元素插入点', 'aid=', aid)
            return
        }
        link = document.createElement('a')
        link.target = '_blank'
        link.className = 'show-bili-aid'
        setData()
        // vue 你赢了,我用 shadow dom 你总碰不着了吧?
        if (!anchor.shadowRoot) {
            const clone = anchor.cloneNode(true)
            anchor.attachShadow({ mode: 'open' })
            anchor.shadowRoot.appendChild(clone)
        }
        const style = document.createElement('style')
        if (!anchor.classList.contains('pub-info')) {
            // 普通视频
            style.textContent = `
                a.show-bili-aid {
                    margin-left: 16px;
                    color: #999;
                    text-decoration: none;
                }
            `
        } else {
            // 番剧,使用 .av-link 的样式
            style.textContent = `
               .pub-info {
                   display: block;
                   float: left;
                   height: 16px;
                   line-height: 16px;
                   margin-right: 10px;
               }
               a.show-bili-aid {
                   display: block;
                   float: left;
                   height: 16px;
                   line-height: 16px;
                   color: #212121;
                   text-decoration: none;
               }
               a.show-bili-aid:hover {
                   color: #03a0d6;
               }
            `
        }
        anchor.shadowRoot.appendChild(style)
        anchor.shadowRoot.appendChild(link)
    }
    const target = window.__INITIAL_STATE__ && 'aid' in window.__INITIAL_STATE__ ? window.__INITIAL_STATE__ : window
    if (target === window && !('aid' in window)) window.aid = ''
    aid = target.aid
    // 我可能比 vue hook 得早,也可能比它晚
    // 如果我比 vue 早,vue 的 hook 触发时会自动触发我的 hook
    // 如果我比 vue 晚,我的 hook 触发时就需要触发 vue 的 hook
    const desc = Object.getOwnPropertyDescriptor(target, 'aid')
    const vueHook = desc.set
    Object.defineProperty(target, 'aid', {
        get: desc.get || (() => aid),
        set(id) {
            aid = id
            setTimeout(run)
            if (vueHook) vueHook.call(this, id)
        },
        enumerable: true,
        configurable: true,
    })
    // 如果已经有 aid 了就开始第一次运行
    if (aid) run()
})()