Greasy Fork

Greasy Fork is available in English.

Bilibili视频剩余时长计算器

计算Bilibili视频剩余时长。

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name Bilibili视频剩余时长计算器
// @namespace http://tampermonkey
// @version 1.0
// @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`;
        }
    }
})();