- // ==UserScript==
- // @name BiliBili-TextFavList
- // @name:zh-CN BiliBili-文字收藏夹列表
- // @namespace https://github.com/Mehver
- // @version 1.0
- // @description (Thanks to ZEP's paid customization) Display Bilibili favourite list results in a text list, which is convenient for sorting by each column.
- // @description:zh-CN (感谢闲鱼买家ZEP的有偿定制) 用纯文字列表的方式展示B站收藏夹结果,方便按各列排序。
- // @sponsor ZEP
- // @author https://github.com/Mehver
- // @icon data:image/png;base64,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
- // @match http*://space.bilibili.com/*/favlist*
- // @exclude http*://space.bilibili.com/*/favlist*ctype*
- // @license MPL-2.0
- // @license^ Mozilla Public License 2.0
- // @charset UTF-8
- // @homepageURL https://github.com/SynRGB/BiliBili-TextFavList
- // @contributionURL https://github.com/SynRGB/BiliBili-TextFavList
- // @copyright Copyright © 2022-PRESENT, Mehver (https://github.com/Mehver)
- // @grant GM_addStyle
- // @grant GM_getResourceText
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // @resource DataTablesCSS https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css
- // ==/UserScript==
-
- let table_font_size = GM_getValue('table_font_size', 16);
- let network_delay = GM_getValue('network_delay', 400);
-
- GM_registerMenuCommand('设置表格字体大小', async () => {
- let newFontSize = prompt('请输入新的字体大小(单位px):', table_font_size);
- if (newFontSize) {
- table_font_size = newFontSize;
- await GM_setValue('table_font_size', table_font_size);
- alert('字体大小已更新!请刷新页面以查看更改。');
- }
- });
-
- GM_registerMenuCommand('设置脚本加载时延', async () => {
- let newNetworkDelay = prompt('脚本依赖外部组件,对于不同的网络环境和电脑性能,\n组件加载速度也不同。如果出现显示错误、白屏等BUG,\n可以适度调高这个数字确保外部引入的组件完成加载。\n默认400(单位ms):', network_delay);
- if (newNetworkDelay) {
- network_delay = newNetworkDelay;
- await GM_setValue('network_delay', network_delay);
- alert('脚本加载时延已更新!请刷新页面以查看更改。');
- }
- });
-
- //////////////////////////////////////
- //////////// DataTables //////////////
- let cssTxt = GM_getResourceText("DataTablesCSS");
- GM_addStyle(cssTxt);
- let head = document.head || document.getElementsByTagName('head')[0];
- let link = document.createElement('link');
- link.type = 'text/css';
- link.rel = 'stylesheet';
- link.href = 'https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css';
- head.appendChild(link);
- (function() {
- let jQueryScript = document.createElement("script");
- jQueryScript.src = "https://code.jquery.com/jquery-3.6.0.min.js";
- jQueryScript.onload = () => {
- let dtScript = document.createElement("script");
- dtScript.src = "https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js";
- // 加载完成后首次运行
- dtScript.onload = () => {
- main();
- };
- document.body.appendChild(dtScript);
- };
- document.body.appendChild(jQueryScript);
- })();
- //////////// DataTables //////////////
- //////////////////////////////////////
-
- //////////////////////////////////////
- /////////////// 触发器 ////////////////
- // 延时避免在 dtScript 和 jQueryScript 加载完成前就运行
- setTimeout(function() {
- setTimeout(function() {
- main();
- }, network_delay / 2);
- let observerElement = new window.MutationObserver(function() {
- setTimeout(function() {
- main();
- }, network_delay / 3);
- });
- // 收藏夹切换触发
- let targetForObserver1 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.favList-info');
- if (targetForObserver1) {
- observerElement.observe(targetForObserver1, {
- childList: true,
- subtree: true,
- characterData: true
- });
- }
- // 手动选择分区或收藏时间
- let targetElement2 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-header.fav-header-info > div > div');
- if (targetElement2) {
- targetElement2.addEventListener('click', function() {
- setTimeout(function() {
- main();
- }, network_delay / 2);
- });
- }
- // 翻页触发
- let targetElement3 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-content.section > ul.be-pager');
- if (targetElement3) {
- targetElement3.addEventListener('click', function() {
- setTimeout(function() {
- main();
- }, network_delay / 2);
- });
- }
- // 搜索触发
- let targetElement4 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-header.fav-header-info > div > div > div.filter-item.search');
- if (targetElement4) {
- targetElement4.addEventListener('click', function() {
- setTimeout(function() {
- main();
- }, network_delay * 2);
- });
- }
- // 订阅等其他类型的收藏夹,点击后应避免脚本生效
- let excludeElement1 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-sidenav > div:nth-child(2)');
- if (excludeElement1) {
- excludeElement1.addEventListener('click', function() {
- setTimeout(function() {
- revert_main();
- }, network_delay / 2);
- });
- }
- let excludeElement2 = document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-sidenav > div:nth-child(3)');
- if (excludeElement2) {
- excludeElement2.addEventListener('click', function() {
- setTimeout(function() {
- revert_main();
- }, network_delay / 2);
- });
- }
- }, network_delay * 2);
- /////////////// 触发器 ////////////////
- //////////////////////////////////////
-
- /////////////////////////////////////
- /////////////// main ////////////////
- function revert_main() {
- try {
- // 隐藏列表
- try { document.querySelector('#biliResultsTable').style.display = 'none'; } catch (e) {}
- // 显示收藏夹
- try { document.querySelector("#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-content.section > ul.fav-video-list.clearfix.content").style.display = 'block'; } catch (e) {}
- } catch (e) {
- console.log(e);
- }
- }
- function main() {
- try {
- // 隐藏”批量操作“按钮
- try { document.querySelector('#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-header.fav-header-info > div > div > div.filter-item.do-batch').style.display = 'none'; } catch (e) {}
- // 隐藏收藏夹
- try { document.querySelector("#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-content.section > ul.fav-video-list.clearfix.content").style.display = 'none'; } catch (e) {}
-
- // Create table with thead for DataTables
- let table = document.createElement('table');
- table.id = "biliResultsTable";
- let thead = document.createElement('thead');
- let tbody = document.createElement('tbody');
- let header = ["收藏于", "时长", "标题"];
- let trHead = document.createElement('tr');
- header.forEach(text => {
- let th = document.createElement('th');
- th.textContent = text;
- trHead.appendChild(th);
- });
- thead.appendChild(trHead);
-
- setTimeout(function() {
- let videoCards = document.querySelectorAll('li');
- videoCards.forEach(videoCard => {
- let title;
- let length;
- let pubdate;
-
- try {
- title = videoCard.querySelector('a.title')?.textContent.trim();
- length = videoCard.querySelector('span.length')?.textContent.trim();
- pubdate = videoCard.querySelector('div.meta.pubdate')?.textContent.trim().replace('收藏于: ', '');
- // let link_video = videoCard.querySelectorAll('a')[0].getAttribute('href');
- } catch (e) {}
-
- let tr = document.createElement('tr');
-
- // 确保没有为空的数据
- if (
- (title !== undefined) &&
- (length !== undefined) &&
- (pubdate !== undefined)
- // (link_video !== undefined)
- ) {
- [pubdate, length, title].forEach(text => {
- let td = document.createElement('td');
- td.textContent = text;
- tr.appendChild(td);
- });
- // let tdTitle = tr.querySelector('td:nth-child(3)');
- // tdTitle.innerHTML = `<a href="${link_video}" target="_blank">${title}</a>`;
- // b230815.02 时长加粗
- tr.querySelector('td:nth-child(2)').style.fontWeight = 'bold';
- // b230815.02 标题用 `#00AEEC` 颜色
- tr.querySelector('td:nth-child(3)').style.color = '#00AEEC';
- tbody.appendChild(tr);
- }
- });
- table.appendChild(thead);
- table.appendChild(tbody);
-
- // 回调获取异步数据,适用于异步加载
- if (tbody.childElementCount === 0) {
- main();
- return;
- }
-
- let tables = document.querySelectorAll('#biliResultsTable');
- if (tables.length === 0) {
- let targetDiv_bott = document.querySelector("#page-fav > div.col-full.clearfix.master > div.fav-main.section > div.fav-content.section > ul.be-pager");
- targetDiv_bott.parentNode.insertBefore(table, targetDiv_bott);
- } else {
- tables[0].parentNode.replaceChild(table, tables[0]);
- }
-
-
- // DataTables 的自定义排序算法
- $.fn.dataTable.ext.type.order['length-sort-pre'] = function (d) {return convertLength(d);};
- $.fn.dataTable.ext.type.order['pubdate-sort-pre'] = function (d) {return convertPubDate(d);};
- // Initialize DataTables
- $(table).DataTable({
- "paging": false,
- "searching": false,
- "info": false,
- "columnDefs": [
- { "type": "length-sort", "targets": 1 },
- { "type": "pubdate-sort", "targets": 0 }
- ]
- });
-
- // b230815.02 去掉底边横线
- GM_addStyle("table.dataTable.no-footer { border-bottom: 0px none !important; }");
- // b230815.02 去掉表头横线 (因CSS复杂,所以创建白色色块覆盖)
- GM_addStyle(".dataTable thead th { border-bottom: 0px none !important; }");
- // b230815.02 调大字号
- GM_addStyle(`.dataTable { font-size: ${table_font_size}px !important; }`);
- // 左侧留30px空白
- GM_addStyle(`.dataTable { margin-left: 30px !important; }`);
- }, 100);
- } catch (e) {
- console.log(e);
- }
- }
- /////////////// main ////////////////
- /////////////////////////////////////
-
- ///////////////////////////////////
- /////// DataTable 的排序算法 ////////
- function convertLength(duration) {
- let parts = duration.split(':').map(part => parseInt(part, 10));
- if (parts.length === 3) {
- return parts[0] * 3600 + parts[1] * 60 + parts[2];
- } else if (parts.length === 2) {
- return parts[0] * 60 + parts[1];
- } else {
- return NaN;
- }
- }
- function convertPubDate(date) {
- const now = new Date();
- if (date.includes('刚刚')) {
- return now.getTime();
- }
- if (date.includes('小时前')) {
- const hoursAgo = parseFloat(date.replace('小时前', ''));
- return now - hoursAgo * 3600 * 1000; // Convert hours to milliseconds
- }
- if (date === "昨天") {
- return now - 24 * 3600 * 1000; // 24 hours in milliseconds
- }
- if (date.includes('-')) {
- const parts = date.split('-').map(part => {
- return part.padStart(2, '0');
- });
- // If only month and day are given, use the current year.
- if (parts.length === 2) {
- parts.unshift(now.getFullYear().toString());
- }
- // Create a new Date object and return its time value in milliseconds
- return new Date(parts.join('-')).getTime();
- }
- }
- /////// DataTable 的排序算法 ////////
- ///////////////////////////////////
-
- console.log("JS script BiliBili-TextFavList (BiliBili-文字收藏夹列表) loaded. See more details at https://github.com/SynRGB/BiliBili-TextFavList");
-