Greasy Fork is available in English.

哔哩哔哩视频页功能拓展

调整B站视频页面的布局展示内容,拓展视频页中的功能

// ==UserScript==
// @name        哔哩哔哩视频页功能拓展
// @namespace   https://tampermonkey.net/
// @homepage    https://space.bilibili.com/473239155/dynamic
// @license     Apache-2.0
// @version     0.2.1
// @description 调整B站视频页面的布局展示内容,拓展视频页中的功能
// @author      byhgz
// @match       https://www.bilibili.com/video/*
// @match       https://www.bilibili.com/list/*
// @icon        https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @require     https://greasyfork.org/scripts/462234-message/code/Message.js?version=1170653
// @require     https://cdn.jsdelivr.net/npm/vue@2
// @require     https://update.greasyfork.org/scripts/516282/1483103/Drawer_gz%E9%A1%B5%E9%9D%A2%E4%BE%A7%E8%BE%B9%E6%8A%BD%E5%B1%89%E7%BB%84%E4%BB%B6.js
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @grant       GM_registerMenuCommand
// @noframes    
// ==/UserScript==
"use strict";
const Tips = {
    info(text, config) {
        Qmsg.info(text, config);
    },
    infoBottomRight(text) {
        this.info(text, {position: "bottomright"});
    },
    success(text, config) {
        Qmsg.success(text, config);
    },
    successBottomRight(text) {
        this.success(text, {position: "bottomright"});
    },
    error(text, config) {
        Qmsg.error(text, config);
    },
    errorBottomRight(text) {
        this.error(text, {position: "bottomright"});
    }
};
const Utils = {
    addGMMenu(text, func, shortcutKey = null) {
        return GM_registerMenuCommand(text, func, shortcutKey);
    },
    addStyle(cssStyleStr) {
        GM_addStyle(cssStyleStr);
    },
    getCurrentUrl() {
        return window.location.href;
    },
};
Utils.addStyle(`
button[gz_type] {
    display: inline-block;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    background: #fff;
    border: 1px solid #dcdfe6;
    color: #606266;
    -webkit-appearance: none;
    text-align: center;
    box-sizing: border-box;
    outline: none;
    margin: 0;
    transition: .1s;
    font-weight: 500;
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    padding: 12px 20px;
    font-size: 14px;
    border-radius: 4px;
}
button[gz_type="primary"] {
    color: #fff;
    background-color: #409eff;
    border-color: #409eff;
}
button[gz_type="success"] {
    color: #fff;
    background-color: #67c23a;
    border-color: #67c23a;
}
button[gz_type="info"] {
    color: #fff;
    background-color: #909399;
    border-color: #909399;
}
button[gz_type="warning"] {
    color: #fff;
    background-color: #e6a23c;
    border-color: #e6a23c;
}
button[gz_type="danger"] {
    color: #fff;
    background-color: #f56c6c;
    border-color: #f56c6c;
}
button[border] {
    border-radius: 20px;
    padding: 12px 23px;
}
`);
const global_BiliBIli_video_page_settings_dataVue = new Vue({
    data() {
        return {
            isDefPlayVideoPage: false,
            isCheckBackLaterPlayVideoPage: false,
        }
    },
    watch: {
    },
    created() {
        this.isDefPlayVideoPage = window.location.href.includes("www.bilibili.com/video/");
        this.isCheckBackLaterPlayVideoPage = window.location.href.includes("www.bilibili.com/list/ml");
    }
});
const drawerGz = new Drawer_gz({
    show: false,
    height: "300px",
    direction: "bottom",
    externalButtonText: "视频页设置",
    externalButtonWidth: "80px",
    backgroundColor: "#ffffff",
    headerShow: false,
    drawerBorder: "1px solid #00f3ff",
});
drawerGz.setBodyHtml(`<div id="video_page_settings_app"></div>`);
const searchDrawerGz = new Drawer_gz({
    show: false,
    direction: "left",
    width: "auto",
    externalButtonText: "搜索",
    externalButtonWidth: "80px",
    backgroundColor: "#ffffff",
    title:"搜索合集中视频",
    drawerBorder: "1px solid #00f3ff",
    zIndex:1499,
    externalButtonShow: false,
});
searchDrawerGz.setBodyHtml(`<div id="video_page_settings_app_search"></div>`);
new Vue({
    template: `
      <div>
        <div v-if="isDefPlayVideoPage">
          <button gz_type @click="listHighAdaptationBut">视频页合集列表自适应高度</button>
          <button gz_type @click="listHighlyRestoredBut">视频页合集列表高度复原</button>
        </div>
        <div v-if="isCheckBackLaterPlayVideoPage">
          <button gz_type @click="listHighAdaptationCheckBackLaterBut">视频页合集列表自适应高度(稍后再看)</button>
          <button gz_type @click="listHighCheckBackLaterRestoredBut">视频页合集列表高度(稍后再看)</button>
        </div>
        <div>
          <button gz_type @click="openSearchDrawerGzBut">打开合集搜索栏</button>
        </div>
      </div>`,
    el: "#video_page_settings_app",
    data() {
        return {
            isDefPlayVideoPage: false,
            isCheckBackLaterPlayVideoPage: false,
            defVideoListMaxHeight: null,
            videoCheckBackLaterListBodyMaxHeight: null,
            videoCheckBackLaterListMaxHeight: null,
        }
    },
    methods: {
        openSearchDrawerGzBut() {
            searchDrawerGz.show(true);
            drawerGz.show(false);
        },
        listHighAdaptationBut() {
            getDefVideoPlayPageListEl().then(el => {
                if (this.defVideoListMaxHeight === null) {
                    this.defVideoListMaxHeight = el.style.maxHeight;
                }
                el.style.maxHeight = "max-content";
                Tips.successBottomRight("已自适应高度!")
            })
        },
        listHighlyRestoredBut() {
            if (this.defVideoListMaxHeight === null) {
                Tips.errorBottomRight("未自适应高度!无需调整.");
                return;
            }
            getDefVideoPlayPageListEl().then(el => {
                el.style.maxHeight = this.defVideoListMaxHeight;
            })
            Tips.successBottomRight("已复原视频页合集列表高度");
        },
        listHighAdaptationCheckBackLaterBut() {
            getCheckBackLaterVideoListElements().then(({bodyList, listEl, defList}) => {
                if (this.videoCheckBackLaterListBodyMaxHeight === null) {
                    this.videoCheckBackLaterListBodyMaxHeight = bodyList.style.maxHeight;
                    this.videoCheckBackLaterListMaxHeight = listEl.style.maxHeight;
                }
                for (let el of defList) {
                    el.style.maxHeight = "max-content";
                }
            })
        },
        listHighCheckBackLaterRestoredBut() {
            if (this.videoCheckBackLaterListBodyMaxHeight === null) {
                Tips.errorBottomRight("未自适应高度!无需调整.");
                return;
            }
            getCheckBackLaterVideoListElements().then(({bodyList, listEl}) => {
                bodyList.style.maxHeight = this.videoCheckBackLaterListBodyMaxHeight;
                listEl.style.maxHeight = this.videoCheckBackLaterListMaxHeight;
            });
        }
    },
    created() {
        this.isDefPlayVideoPage = global_BiliBIli_video_page_settings_dataVue["isDefPlayVideoPage"];
        this.isCheckBackLaterPlayVideoPage = global_BiliBIli_video_page_settings_dataVue["isCheckBackLaterPlayVideoPage"];
    }
});
const getDefVideoPageCollectionElList = async () => {
    return new Promise(resolve => {
        const i1 = setInterval(() => {
            const elList = document.querySelectorAll(".video-pod__body>.video-pod__list.section>div");
            if (elList.length === 0) return;
            clearInterval(i1);
            const tempList = [];
            for (let el of elList) {
                tempList.push({
                    title: el.querySelector(".title").title.trim(),
                    clickEl: el.querySelector(".single-p>.simple-base-item")
                })
            }
            resolve(tempList)
        }, 500);
    });
}
const getDefVideoPageAnthologyElList = async () => {
    return new Promise(resolve => {
        const i1 = setInterval(() => {
            const elList = document.querySelectorAll(".video-pod__body>.video-pod__list.multip.list>div");
            if (elList.length === 0) return;
            clearInterval(i1);
            const tempList = [];
            for (let el of elList) {
                tempList.push({
                    title: el.querySelector(".title").title.trim(),
                    clickEl: el
                })
            }
            resolve(tempList);
        }, 500);
    })
}
const isDefVideoPageAnthology = (maxIndex = 3) => {
    return new Promise((resolve, reject) => {
        let index = 1;
        const i1 = setInterval(() => {
            if (document.querySelector(".header-top .view-mode") === null) {
                if (index === maxIndex) {
                    clearInterval(i1);
                    reject();
                }
                index++;
                return;
            }
            clearInterval(i1);
            resolve();
        }, 1000);
    })
}
const getCheckBackLaterVideoPageCollectionElList = async () => {
    return new Promise(resolve => {
        const i1 = setInterval(() => {
            const elList = document.querySelectorAll("#playlist-video-action-list>.action-list-inner>div");
            if (elList.length === 0) return;
            clearInterval(i1);
            const tempList = [];
            for (let el of elList) {
                tempList.push({
                    title: el.querySelector(".title").title.trim(),
                    clickEl: el.querySelector(".main")
                })
            }
            resolve(tempList)
        }, 500);
    });
}
const __defVideoPageCollectionElList = [];
new Vue({
    el: "#video_page_settings_app_search",
    template: `
      <div>
        <input type="text" placeholder="搜索列表视频" v-model="inputValue" style="width: 100%">
        <div>
          <div v-for="item in searchShowList">
            <button gz_type @click="playVideo(item)">{{ item.title }}</button>
          </div>
        </div>
      </div>`,
    data() {
        return {
            inputValue: '',
            searchShowList: []
        }
    },
    methods: {
        playVideo(item) {
            item.clickEl.click();
        }
    },
    watch: {
        inputValue(newValue) {
            this.searchShowList.splice(0, this.searchShowList.length);
            for (let data of __defVideoPageCollectionElList) {
                if (data.title.includes(newValue)) {
                    this.searchShowList.push(data);
                }
            }
        }
    },
    created() {
        if (global_BiliBIli_video_page_settings_dataVue["isCheckBackLaterPlayVideoPage"]) {
            getCheckBackLaterVideoPageCollectionElList().then(list => {
                __defVideoPageCollectionElList.push(...list);
                Qmsg.success("已获取到稍后再看视频页合集列表", {position: "bottomright"});
            });
        } else {
            isDefVideoPageAnthology().then(async () => {
                const newVar = await getDefVideoPageAnthologyElList();
                console.log(newVar);
                __defVideoPageCollectionElList.push(...newVar);
                Qmsg.success("已获取到视频选集列表", {position: "bottomright"})
            }).catch(() => {
                getDefVideoPageCollectionElList().then(list => {
                    Qmsg.success("已获取到视频合集列表", {position: "bottomright"});
                    __defVideoPageCollectionElList.push(...list);
                })
            });
        }
    }
});
const getElement = (selector, timeOut = 500) => {
    return new Promise(resolve => {
        const i1 = setInterval(() => {
            const el = document.querySelector(selector);
            if (el === null) return;
            clearInterval(i1);
            resolve(el);
        }, timeOut);
    });
}
const getDefVideoPlayPageListEl = () => {
    return getElement(".video-pod__body");
}
const getCheckBackLaterVideoListElements = async () => {
    const p1 = getElement("#playlist-video-action-list-body");
    const p2 = getElement("#playlist-video-action-list");
    const defList = await Promise.all([p1, p2]);
    [bodyListEl, listEl] = defList;
    return {bodyList: bodyListEl, listEl: listEl, defList: defList}
}