在B站的多P稿件和合集中添加搜索框,进行内容搜索
// ==UserScript==
// @name B站分P搜索
// @namespace https://github.com/LianTianYou
// @version 1.0.2
// @description 在B站的多P稿件和合集中添加搜索框,进行内容搜索
// @author LianTianYou
// @license MIT
// @match https://www.bilibili.com/video/*
// @icon https://www.bilibili.com/favicon.ico
// @grant none
// ==/UserScript==
(() => {
const clearBox = document.createElement("div");
const isDebug = false;
function debug(...msg) {
const isDebug = false;
if (isDebug) {
console.log(...msg);
};
}
/**
* 清空筛选结果
*/
function clearFilter(data) {
if (data == null) return;
if (data.parts == null || data.parts.length === 0) return;
if (data.pages == null || data.pages.length === 0) return;
data.parts.forEach((part, index) => {
data.pages[index].style.display = "flex";
part.innerHTML = part.textContent;
});
/* 定位到正在播放的分P */
// 分P的容器
const listContainer = document.querySelector('.video-pod__body');
// 当前选择的分P
const selectedItem = document.querySelector('.video-pod__item.active');
if (listContainer && selectedItem) {
// const offsetTop = selectedItem.offsetTop - listContainer.offsetTop;
listContainer.scrollTo({ top: selectedItem.offsetTop, behavior: 'instant' });
}
}
/**
* 根据关键字进行筛选
*/
function toggleFilter(data, keyword) {
if (keyword == null || data == null) return;
if (data.parts == null || data.parts.length === 0) return;
if (data.pages == null || data.pages.length === 0) return;
const regex = new RegExp(`(${keyword})`, "ig");
debug(keyword);
debug(regex);
data.parts.forEach((part, index) => {
const page = data.pages[index];
let value = part.textContent;
if(value.search(regex) != -1) {
// 匹配到关键词
page.style.display = "flex";
part.innerHTML = value.replaceAll(regex, '<em class="keyword">$1</em>');
} else {
// 未匹配项
page.style.display = "none";
}
});
}
/**
* 根据搜索框的结果进行处理
*/
function searchFilter(data, keyword = "") {
keyword = keyword.trim();
if (!keyword) {
// 关键词为空
clearFilter(data);
} else {
// 有关键词
toggleFilter(data, keyword);
}
}
/**
* 改变清空按钮的显示状态
*/
function changeClearBtn(value) {
if (!value || value.trim() === "") {
clearBox.style.display = "none";
} else if(clearBox.style.display !== "block") {
clearBox.style.display = "block";
}
}
/**
* 添加 style 标签
*/
function addStyle() {
let styleTag = document.createElement("style");
styleTag.type = "text/css";
styleTag.id = "bili-filter";
let styleCode = `
.search-box {
margin: 5px auto 0 auto;
/* padding: 0 10px; */
background: #F1F2F3;
height: 44px;
display: flex;
align-items: center;
}
.search-box > input.search {
height: 34px;
width: 100%;
padding: 0 10px;
font-size: 14px;
outline: none;
border: 1px solid #e3e5e7;
border-radius: 5px;
caret-color: #5e5e5e;
}
.search-box > input.search:focus {
border: 1px solid #00aeec;
}
.keyword {
color: #f25d8e;
font-style: normal;
}
.clear-box {
display: none;
position: absolute;
right: 16px;
}
.clear-btn {
width: 20px;
height: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.clear-btn > svg {
height: 8px;
width: 8px;
fill: #61666d;
}
`;
styleTag.appendChild(document.createTextNode(styleCode));
document.querySelector("head").appendChild(styleTag);
}
/**
* 创建自定义的 DOM 元素,并返回容器的节点
*/
function createElement(data) {
// 插入 style 标签
addStyle();
// 容器
const searchBox = document.createElement("div");
searchBox.className = "search-box";
// 搜索框
const search = document.createElement("input");
search.type = "search";
search.className = "search";
search.placeholder = "搜索分P...";
// search.addEventListener("change", function(e) {
// searchFilter(data, e.target.value);
// });
search.addEventListener("input", function(e) {
searchFilter(data, e.target.value);
changeClearBtn(e.target.value);
});
searchBox.append(search);
// 清空按钮
clearBox.className = "clear-box";
const clearBtn = document.createElement("div");
clearBtn.className = "clear-btn";
clearBtn.innerHTML = '<svg t="1706028151814" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9157" width="200" height="200"><path d="M632.117978 513.833356l361.805812 361.735298a85.462608 85.462608 0 1 1-121.001515 120.789974L511.116463 634.552816 146.913186 998.756094a86.026718 86.026718 0 0 1-121.706652-121.706652L389.480325 512.775651 27.674513 150.969839A85.392095 85.392095 0 0 1 148.393973 30.250379L510.199785 392.056191l366.671258-366.671258a86.026718 86.026718 0 0 1 121.706652 121.706652z" p-id="9158"></path></svg>';
clearBtn.addEventListener("click", function() {
search.value = "";
searchFilter(data);
changeClearBtn("");
});
clearBox.append(clearBtn);
searchBox.append(clearBox);
return searchBox;
}
/**
* 在普通分P中添加元素
*/
function insertToPages() {
const data = {
pages: document.querySelectorAll(".video-pod__body .video-pod__item.normal"),
parts: document.querySelectorAll(".video-pod__body .video-pod__item.normal div.title-txt")
};
if (data.pages == null || data.pages.length === 0 || data.parts == null || data.parts.length === 0) {
return;
}
// 将元素插入到网页中
const searchBox = createElement(data);
const pageList = document.querySelector(".video-pod__header .header-top"); // 分P列表
if (pageList) {
pageList.after(searchBox);
}
}
/**
* 在合集中添加元素
*/
function insertToSections() {
const data = {
pages: document.querySelectorAll(".video-episode-card"),
parts: document.querySelectorAll(".video-episode-card .video-episode-card__info-title")
};
// 将元素插入到网页中
const searchBox = createElement(data);
// const headCon = document.querySelector(".video-sections-head");
// headCon.after(searchBox);
// document.querySelector(".video-section-list").style.height = "auto";
}
/** 等待元素出现 */
function waitExist(selecter, timeout = 5, check = null) {
let interval = 50;
let count = 0;
const p = new Promise((resolve, reject) => {
const timer = setInterval(() => {
const ele = document.querySelector(selecter);
if (interval * count > timeout * 1000) {
clearInterval(timer);
// debug("等待超时");
reject(new Error("等待超时"));
}
count++;
debug(ele);
if (!ele) return;
if (check && !check(ele)) return;
clearInterval(timer);
resolve(ele);
}, interval);
});
return p;
}
/**
* 入口函数
*/
async function main() {
try {
// 等待弹幕列表出现
await waitExist("#danmukuBox", 5);
debug("#danmukuBox 出现");
// 等待分P列表出现
await waitExist(".video-pod__body", 5);
debug(".video-pod__body 出现");
// 等待分P列表顶部操作栏出现
await waitExist(".video-pod__header > .header-top", 5);
debug(".header-top 出现");
await waitExist(".video-pod__item.active", 5);
// 等待弹幕列表的标题出现
await waitExist("#danmukuBox .bui-dropdown-name", 5);
debug(".bui-dropdown-name 出现");
insertToPages();
// if (document.querySelector(".video-pod__body")) {
// // 等待分P列表出现
// debug(".video-pod__body 出现");
// // 等待分P列表顶部操作栏出现
// await waitExist(".video-pod__header > .header-top", 5);
// debug(".header-top 出现");
// insertToPages();
// } else if(document.querySelector(".base-video-sections-v1")) {
// debug("sections 出现");
// const result = await waitExist(".video-episode-card__info-playing .cur-play-icon", 5, (e) => e.style.display !== "none");
// debug("cur-play-icon 出现");
// debug(result);
// await new Promise((resolve) => requestAnimationFrame(resolve));
// insertToSections();
// requestIdleCallback(() => {
// debug("浏览器空闲");
// // debugger;
// });
// }
} catch(err) {
debug(err);
}
debug("执行完毕");
}
window.onload = () => {
debug("onloaded");
main();
};
})();