Greasy Fork is available in English.
B站合集倒序播放-脚本
当前为
// ==UserScript==
// @name B站合集倒序播放
// @description B站合集倒序播放-脚本
// @version 0.0.1
// @author Grant Howard, Coulomb-G
// @license MIT
// @match *://*.bilibili.com/video/*
// @exclude *://api.bilibili.com/*
// @exclude *://api.*.bilibili.com/*
// @exclude *://*.bilibili.com/api/*
// @exclude *://member.bilibili.com/studio/bs-editor/*
// @exclude *://t.bilibili.com/h5/dynamic/specification
// @exclude *://bbq.bilibili.com/*
// @exclude *://message.bilibili.com/pages/nav/header_sync
// @exclude *://s1.hdslb.com/bfs/seed/jinkela/short/cols/iframe.html
// @exclude *://open-live.bilibili.com/*
// @run-at document-start
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_info
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_addStyle
// @connect raw.githubusercontent.com
// @connect github.com
// @connect cdn.jsdelivr.net
// @connect cn.bing.com
// @connect www.bing.com
// @connect translate.google.cn
// @connect translate.google.com
// @connect localhost
// @connect *
// @namespace http://greasyfork.icu/users/734541
// ==/UserScript==
(() => {
GM_addStyle(`#zaizai-div .video-sections-head_second-line {
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 12px 16px 0;
color: var(--text3);
color: var(--text3);
padding-bottom: 12px;
font-size: 14px;
line-height: 16px;
gap: 10px 20px;
}
#zaizai-div .border-bottom-line {
height: 1px;
background: var(--line_regular);
margin: 0 15px;
}
#zaizai-div .switch-button {
margin: 0;
display: inline-block;
position: relative;
width: 30px;
height: 20px;
border: 1px solid #ccc;
outline: none;
border-radius: 10px;
box-sizing: border-box;
background: #ccc;
cursor: pointer;
transition: border-color .2s, background-color .2s;
vertical-align: middle;
}
#zaizai-div .switch-button.on:after {
left: 11px;
}
#zaizai-div .switch-button:after {
content: "";
position: absolute;
top: 1px;
left: 1px;
border-radius: 100%;
width: 16px;
height: 16px;
background-color: #fff;
transition: all .2s;
}
#zaizai-div .switch-button.on {
border: 1px solid var(--brand_blue);
background-color: var(--brand_blue);
}
#zaizai-div .txt {
margin-right: 4px;
vertical-align: middle;
}
`)
const console = (() => {
const _console = window.console
return {
log: (...args) => {
_console.log(`%c ZAIZAI `,
'padding: 2px 1px; border-radius: 3px; color: #fff; background: #42c02e; font-weight: bold;', ...args)
}
}
})()
// 全局变量
const local = useReactiveLocalStorage({
defaultreverseorder: false,
// 开启倒序播放
startreverseorder: false
})
let Video = null
function useReactiveLocalStorage(obj) {
let data = {}
let zaizaiStore = window.localStorage.getItem('zaizai-store')
if (zaizaiStore) {
zaizaiStore = JSON.parse(zaizaiStore)
for (const key in obj) {
data[key] = zaizaiStore[key] || obj[key]
}
} else {
data = obj
}
let handler = {
set(target, key, value) {
let res = Reflect.set(target, key, value)
try {
window.localStorage.setItem(`zaizai-store`, JSON.stringify(data))
} catch (error) {
console.log('存储失败,请检查浏览器设置', error);
}
return res
},
get(target, key) {
let ret = Reflect.get(target, key)
return typeof ret === 'object' ? new Proxy(ret, handler) : ret
}
}
data = new Proxy(data, handler)
return data
}
function waitTime(callback, options = { time: 500, isSetup: false }) {
let timeout = null
return new Promise((resolve) => {
if (options.isSetup) {
let res = callback()
if (res) resolve(res)
}
timeout = setInterval(() => {
let res = callback()
if (res) {
clearInterval(timeout)
resolve(res)
}
}, options.time)
})
}
async function selectVideo() {
await waitTime(() => {
let video = document.querySelector('video')
if (video) {
Video = video
return true
}
}, {
isSetup: true
})
}
async function VideoOnPlay() {
if (local.startreverseorder && !document.querySelector('#zaizai-div')) {
const div = document.createElement('div')
div.id = 'zaizai-div'
div.innerHTML = `
<div class="video-sections-head">
<div class="border-bottom-line"></div>
<div class="video-sections-head_second-line">
<div>
<span class="txt">默认开启倒序播放</span>
<span id="defaultreverseorder" class="switch-button ${local.defaultreverseorder ? 'on' : ''}"></span>
</div>
<div>
<span class="txt">倒序播放</span>
<span id="startreverseorder" class="switch-button ${local.startreverseorder ? 'on' : ''}"></span>
</div>
</div>
</div>
`
const basesections = await waitTime(() => {
let basev1 = document.querySelector('.base-video-sections-v1')
if (basev1) {
return basev1
}
}, { isSetup: true })
basesections.appendChild(div)
// 默认开启倒序播放
let defaultreverseorder = document.querySelector('#defaultreverseorder')
function defaultSwitchClick() {
local.defaultreverseorder = !local.defaultreverseorder
if (local.defaultreverseorder) {
this.classList.add('on')
} else {
this.classList.remove('on')
}
}
defaultreverseorder.addEventListener('click', defaultSwitchClick)
// 倒序播放
let startreverseorder = document.querySelector('#startreverseorder')
function switchReverseoOnClick() {
local.startreverseorder = !local.startreverseorder
if (local.startreverseorder) {
this.classList.add('on')
Video.addEventListener('ended', VideoOnEnded)
} else {
this.classList.remove('on')
Video.removeEventListener('ended', VideoOnEnded)
}
}
startreverseorder.addEventListener('click', switchReverseoOnClick)
const button = document.querySelector('.video-sections-head_second-line button').cloneNode()
button.textContent = '滚动到当前播放'
button.style.width = '100%'
function scrollToCurrent() {
let { currentEl } = getCurrentcard()
document.querySelector('.video-sections-content-list').scrollTo({
top: currentEl.offsetTop - 150
})
}
button.addEventListener('click', scrollToCurrent)
const newdiv = document.createElement('div')
newdiv.style.width = '100%'
newdiv.appendChild(button)
div.querySelector('.video-sections-head_second-line').appendChild(newdiv)
}
}
function getCurrentcard() {
const episodecards = document.querySelectorAll('.video-episode-card')
let i = 0
for (const element of episodecards) {
let curicon = element.querySelector('.cur-play-icon')
if (curicon.style.display !== 'none') {
break
}
i++
}
// 顺序上一个
let previous = i - 1 <= 0 ? episodecards.length - 1 : i - 1
// 顺序下一个
let next = i + 1 >= episodecards.length - 1 ? episodecards.length - 1 : i + 1
return {
elements: episodecards,
current: i,
currentEl: episodecards[i],
next,
nextEl: episodecards[next],
previous,
previousEl: episodecards[previous]
}
}
function VideoOnEnded() {
/* let curpage = document.querySelector('.cur-page').textContent
curpage = curpage.match(/\d+/g).at(-1)
curpage = parseInt(curpage) */
const { previousEl } = getCurrentcard()
previousEl.click()
}
async function main() {
console.log('mian start');
let is_base_video_sections_v1 = null
await waitTime(() => {
let progress = document.querySelector('.bpx-player-progress-schedule-current')
if (progress) {
let transform = progress.style.transform.replace('scaleX(', '').replace(')', '')
if (transform > 0) {
is_base_video_sections_v1 = document.querySelector('.base-video-sections-v1')
return true
}
}
})
if (!is_base_video_sections_v1) {
console.log('mian stop 没有合集');
return
}
await selectVideo()
Video.addEventListener('play', VideoOnPlay)
if (local.defaultreverseorder) {
Video.addEventListener('ended', VideoOnEnded)
}
VideoOnPlay()
console.log('mian stop 成功开启');
}
window.onload = () => {
console.log('正式-v2');
main()
}
})()