BiliBili-文字收藏夹列表

(感谢闲鱼买家ZEP的有偿定制) 用纯文字列表的方式展示B站收藏夹结果,方便按各列排序。

// ==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");

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址