Greasy Fork

Greasy Fork is available in English.

Bilibili视频剩余时长计算器

计算Bilibili视频剩余时长。

当前为 2023-04-03 提交的版本,查看 最新版本

// ==UserScript==
// @name Bilibili视频剩余时长计算器
// @namespace http://tampermonkey
// @version 1.1
// @description 计算Bilibili视频剩余时长。
// @author txsxcy
// @match https://www.bilibili.com/video/*
// @grant none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Define CSS style rules
    const style = `
     .video-info {
        overflow: hidden;
        text-align: center;
        box-sizing: border-box;
        height: 100%;
        width: 100%;
        background-color: rgb(241, 242, 243);
        border-radius: 6px;
        font-size: 15px;
        line-height: 30px;
        margin-bottom: 25px;
        padding: 10px 10px 0px 10px;
        pointer-events: all;
    }

    .video-info li {
        width: 30%;
        float: left;
        margin-right: 10px;
        margin-bottom: 10px;
        list-style: none;
    }

    .video-info ul li:hover {
        background-color: rgb(255, 255, 255);
        border-radius: 12px;
        color: #00aeec;
        cursor:pointer

    }

    .video-info ul li:hover span {
        color: #00aeec;
    }

    .video-info span {
        display: block;
        width: 100%;
    }

    .video-info li span:first-child {
        color: #222;
        font-weight: 700;
    }

    .video-info li span:last-child {
        font-size: 12px;
        color: #666;
    }
    `;

    // Create a new style element and append it to the head of the document
    const styleEl = document.createElement('style');
    styleEl.textContent = style;
    document.head.appendChild(styleEl);
})();

(function () {
    'use strict';

    // 选择要监听的元素
    const targetNodes = document.querySelectorAll('.head-left, .html_player_enhance_tips');

    // 创建一个 MutationObserver 实例
    const observer = new MutationObserver(function (mutationsList) {
        // 遍历所有变化记录
        for (const mutation of mutationsList) {
            // 如果变化是目标节点的innerText发生变化
            if (mutation.type === 'childList' && mutation.target.innerText !== mutation.oldValue) {
                // 执行函数abc()
                abc();
            }
        }
    });

    // 配置观察选项
    const config = { attributes: false, childList: true, subtree: true, characterDataOldValue: true };

    // 开始观察目标节点
    for (const targetNode of targetNodes) {
        observer.observe(targetNode, config);
    }

    function abc() {

        let currentspeed = parseFloat(document.querySelector(".html_player_enhance_tips").innerText.match(/播放速度:(\d+(\.\d+)?)/)[1]);
        let matches = document.querySelector('.cur-page').innerText.match(/\((\d+)\/(\d+)\)/);
        let start = parseInt(matches[1]);
        let end = parseInt(matches[2]);
        let videoData = document.querySelector('#danmukuBox');
        let duration = calTime(start, end);
        let totalDuration = durationToString(calTime(1, end).total);
        let watchedDuration = durationToString(calTime(1, start - 1).total);
        let remainingDuration = durationToString(calTime(start, end).total);
        let remainingDuration2x = durationToString(Math.floor(duration.total / 2));
        let remainingDuration3x = durationToString(Math.floor(duration.total / 3));
        let remainingDurationXx = durationToString(Math.floor(duration.total / currentspeed));


        // 获取要插入的元素的父元素
        let parent = videoData.parentElement;
        // 查找是否有类名为 "video-info" 的元素
        let info = parent.querySelector(".video-info");
        // 如果存在,则删除它
        if (info) {
            info.remove();
        }
        const html = ` <div>
        <ul>
            <li>
                <span>总时长</span>
                <span>${totalDuration}</span>
            </li>
            <li>
                <span>已看时长</span>
                <span>${watchedDuration}</span>
            </li>
            <li>
                <span>剩余时长</span>
                <span>${remainingDuration}</span>
            </li>
            <li>
                <span>2x</span>
                <span>${remainingDuration2x}</span>
            </li>
            <li>
                <span>3x</span>
                <span>${remainingDuration3x}</span>
            </li>
            <li>
                <span>${currentspeed}x</span>
                <span>${remainingDurationXx}</span>
            </li>
        </ul>
    </div>`;
        videoData.insertAdjacentHTML('afterend', `<div class="video-info">${html}</div>`);

    }
    window.onpopstate = function (event) {
        // 调用 ab 函数
        abc();
    };
    setTimeout(() => abc, 3000);

    function calTime(start, end) {
        const lis = document.querySelectorAll('.list-box .duration');
        const duration = { total: 0, watched: 0, remaining: 0 };

        lis.forEach((currentValue, index) => {
            if (index >= start - 1 && index <= end - 1) {
                const time = currentValue.innerText.replace(/\.\d+/g, ''); // 去除小数点
                const timeArr = time.split(':');
                let timeSeconds = 0;

                if (timeArr.length == 3) {
                    timeSeconds += Number(timeArr[0]) * 60 * 60;
                    timeSeconds += Number(timeArr[1]) * 60;
                    timeSeconds += Number(timeArr[2]);
                } else {
                    timeSeconds += Number(timeArr[0]) * 60;
                    timeSeconds += Number(timeArr[1]);
                }

                if (index < end - 1) {
                    duration.watched += timeSeconds;
                } else {
                    duration.remaining += timeSeconds;
                }
                duration.total += timeSeconds;
            }
        });

        return duration;
    }

    function durationToString(duration) {
        const h = parseInt(duration / 3600);
        const m = parseInt(duration / 60) % 60;
        const s = duration % 60;

        if (h > 0) {
            return `${h}h ${m}min ${s}s`;
        } else {
            return `${m}min ${s}s`;
        }
    }
})();