// ==UserScript==
// @name Bilibili 显示视频分区、av号等
// @namespace https://space.bilibili.com/517723/
// @version 0.02
// @description 在视频下方显示 分区、av 号、分P标题、动态
// @author 僠儖僲
// @include https://www.bilibili.com/video/*
// @icon https://www.bilibili.com/favicon.ico
// @compatible firefox Firefox78-esr
// @compatible edge Edge88
// @noframes
// @run-at document-start
// ==/UserScript==
(()=>{
'use strict';
const list=['详细信息','分P标题','动态']
const isAppend=0//为1则在简介下方
const ExterminatePJAX=false//关闭PJAX
const root=window.unsafeWindow||window
const globalKey='__BILIBILI_VIEWMODEL_GLOBAL_PROMISE__'
const vmp=root[globalKey]||(root[globalKey]=(()=>{
const once=(t,s,cb)=>new Promise(ok=>{
cb=e=>{t.removeEventListener(s,cb,1);ok(e)}
t.addEventListener(s,cb,1)
})
const doc=document,$=doc.querySelector.bind(doc),rAF=cb=>setTimeout(cb,500)
const waitElem=select=>new Promise((resolve,reject)=>{
const next=()=>{try{
el=$(select)
el?resolve(el):rAF(next)
}catch(e){reject(e)}}
var el;next()
})
const waitVm=app=>new Promise(ok=>{
const vm=app.__vue__
if(vm){ok(vm);return}
const set=vm=>{
if(!vm)return
ok(vm)
Object.defineProperty(app,'__vue__',{
configurable: true,
enumerable: true,
value: vm,
writable: true
})
}
Object.defineProperty(app,'__vue__',{
configurable: true,
enumerable: true,
get(){return void 0},
set(vm){set(vm)}
})
})
const p=once(doc,'DOMContentLoaded')
.then(e=>waitElem('#app,#viewlater-app,.app-wrap'))
.then(waitVm)
p.then(vm=>{p.vm=vm})
Object.assign(p,{once,$})
return p
})())
const _call=Function.prototype.call,
saftyArrow=fn=>_call.bind(fn,null),
eventStopPropagation=_call.bind(Event.prototype.stopPropagation)
const getEl=(value,key='title')=>{
var el=document.querySelector(`.video-desc>.info[${key}="${value}"]`)
if(!el){
el=document.createElement('div')
el.className='info open'
el.setAttribute(key,value)
el.setAttribute('data-old',location.href)
el.addEventListener('click',eventStopPropagation,1)
document.querySelector('.video-desc')[isAppend?'append':'prepend'](el)
}
return el
}
var curPart=null,curPartQuery=''
const map={
['详细信息'](el,data){
var av='av'+data.aid
var list=[
'分区:'+data.tname,av,data.bvid//,'cid='+root.cid
]
el.innerHTML=list.join('\u3000')
.replace(/av(\d+)(?!\w)/gi,`<a href="/video/av$1/${curPartQuery}" target="_blank">av$1</a>`)
.replace(/(bv1)(\w{9})/gi,`<a href="/video/bv1$2/${curPartQuery}" target="_blank">$1$2</a>`)
},
['分P标题'](el,data){
var page=data.pages[curPart?(curPart[1]-1):0]||data.pages[0]
var title=page&&page.part
el.innerText=title==data.title?'分P标题与标题相同':(title?'分P标题:'+title:'分P标题为空')
},
eds:'—'.repeat(5),
['动态'](el,data){
el.innerText=data.dynamic?`${this.eds}动态${this.eds}\n${data.dynamic}`:'————动态为空————'
}
}
const inject=saftyArrow((data)=>{
data||(data=root.__INITIAL_STATE__.videoData)
curPart=location.search.match(/[?&]p=(\d+)/)
curPartQuery=curPart?('?p='+curPart[1]):''
var b=getEl('分割线').style
b.height='1px'
b.backgroundColor="rgb(229, 233, 240)"
b.margin='15px 0'
list[isAppend?'reduce':'reduceRight']((acc,cur)=>{
map[cur](getEl(cur),data)
},void 0)
})
vmp.then(vm=>{
if(ExterminatePJAX){
vm.$router.beforeEach((to,from,next)=>{
var {p}=to.query
p=p?'?p='+p:''
location.href=to.path+p
next(false)
})
}
vm.$watch('videoData',inject)
inject(vm.videoData)
})
})()