bilibili 显示 av 号

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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