计算B站视频列表中到指定集数的剩余时长
当前为
// ==UserScript==
// @name B站视频时长计算器
// @namespace http://tampermonkey.net/
// @version 0.8
// @description 计算B站视频列表中到指定集数的剩余时长
// @author Your name
// @match https://www.bilibili.com/video/*
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 将时长从MM:SS转换为秒
function parseDuration(duration) {
const [minutes, seconds] = duration.split(':').map(Number);
return minutes * 60 + seconds;
}
// 将总秒数转换为HH:MM:SS格式
function formatDuration(totalSeconds) {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}
// 获取当前播放的集数
function getCurrentEpisode() {
const urlParams = new URLSearchParams(window.location.search);
const p = urlParams.get('p');
return p ? parseInt(p) : 1;
}
// 主要计算逻辑
function calculateTotalDuration(startPage) {
// 更新选择器以匹配B站视频列表的时长元素
const videoItems = document.querySelectorAll('.video-episode-card__info-duration');
let totalSeconds = 0;
let totalEpisodes = videoItems.length;
// 如果没有找到视频元素,返回空结果
if (totalEpisodes === 0) {
console.log('未找到视频时长元素,尝试其他选择器');
// 尝试备用选择器
const alternativeItems = document.querySelectorAll('.duration');
if (alternativeItems.length === 0) {
return {
duration: '0:00:00',
totalEpisodes: 0,
remainingEpisodes: 0
};
}
videoItems = alternativeItems;
totalEpisodes = alternativeItems.length;
}
console.log('找到视频元素数量:', totalEpisodes);
// 确保起始页码有效
startPage = Math.max(1, Math.min(startPage, totalEpisodes));
// 计算从起始页到最后的总时长
let remainingEpisodes = 0;
videoItems.forEach((item, index) => {
const pageNum = index + 1;
// 只计算从起始页(不包含)到最后的时长
if (pageNum > startPage) { // 改为大于而不是大于等于
const duration = item.textContent.trim();
console.log(`第${pageNum}集时长:`, duration);
totalSeconds += parseDuration(duration);
remainingEpisodes++;
}
});
const result = {
duration: formatDuration(totalSeconds),
totalEpisodes: totalEpisodes,
remainingEpisodes: remainingEpisodes
};
console.log('计算结果:', result);
return result;
}
// 创建控件
function createControls() {
const container = document.createElement('div');
container.style.cssText = `
display: inline-flex;
gap: 10px;
align-items: center;
margin-left: 20px;
vertical-align: middle;
`;
// 创建计算按钮(左)
const button = document.createElement('button');
button.textContent = '计算剩余时长';
button.style.cssText = `
padding: 5px 10px;
background: #00A1D6;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 14px;
order: 1;
`;
// 创建输入框(中)
const input = document.createElement('input');
input.type = 'number';
input.placeholder = '目标集数';
input.style.cssText = `
width: 80px;
padding: 5px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 14px;
text-align: center;
order: 2;
`;
// 创建结果显示框(右)
const resultBox = document.createElement('div');
resultBox.style.cssText = `
display: none;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.7);
color: white;
border-radius: 3px;
font-size: 14px;
line-height: 1.5;
white-space: nowrap;
vertical-align: middle;
order: 3;
`;
container.appendChild(button);
container.appendChild(input);
container.appendChild(resultBox);
const titleContainer = document.querySelector('.video-info-title-inner');
if (titleContainer) {
titleContainer.appendChild(container);
}
return { button, input, resultBox };
}
// 更新显示
function updateDisplay(resultBox, input) {
const currentPage = parseInt(input.value) || getCurrentEpisode();
const result = calculateTotalDuration(currentPage);
if (result.totalEpisodes === 0) {
resultBox.textContent = '未找到视频时长信息';
} else if (currentPage >= result.totalEpisodes) {
resultBox.textContent = '已经是最后一集了';
} else {
resultBox.textContent = `从第${currentPage}集之后,还有${result.remainingEpisodes}集,` +
`总时长: ${result.duration}`;
}
resultBox.style.display = 'block';
}
// 初始化
function init() {
// 等待页面加载完成
setTimeout(() => {
const { button, input, resultBox } = createControls();
// 设置初始值并显示
const currentEpisode = getCurrentEpisode();
input.value = currentEpisode;
updateDisplay(resultBox, input);
// 点击按钮更新
button.addEventListener('click', () => {
updateDisplay(resultBox, input);
});
// 回车更新
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
button.click();
}
});
// URL变化监听
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(() => {
const newEpisode = getCurrentEpisode();
input.value = newEpisode;
updateDisplay(resultBox, input);
}, 1000);
}
}).observe(document, { subtree: true, childList: true });
// 定期检查更新(每秒检查一次)
setInterval(() => {
const newEpisode = getCurrentEpisode();
if (newEpisode !== parseInt(input.value)) {
input.value = newEpisode;
updateDisplay(resultBox, input);
}
}, 1000);
}, 2000);
}
// 启动
window.addEventListener('load', init);
})();