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