Greasy Fork

Greasy Fork is available in English.

B站合集倒序播放

B站合集倒序播放-脚本

当前为 2024-08-20 提交的版本,查看 最新版本

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