计算B站视频列表中到指定集数的剩余时长
当前为
// ==UserScript==
// @name B站视频时长计算器
// @namespace http://tampermonkey.net/
// @version 0.9
// @description 计算B站视频列表中到指定集数的剩余时长
// @author Your name
// @match https://www.bilibili.com/video/*
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 将时长从MM:SS或HH:MM:SS转换为秒
function parseDuration(duration) {
// 清理并标准化时长格式
const cleanDuration = duration.trim().replace(/^0+/, '');
const parts = cleanDuration.split(':').map(Number);
// 检查时长格式是否合理
if (parts.length < 2 || parts.length > 3) {
console.warn('异常时长格式:', duration);
return 0;
}
// 验证每个部分的值是否合理
const isValid = parts.every((part, index) => {
if (isNaN(part)) return false;
if (index === 0 && parts.length === 3) return part < 24; // 小时
return part < 60; // 分钟和秒钟
});
if (!isValid) {
console.warn('异常时长值:', duration);
return 0;
}
// 转换为秒
if (parts.length === 2) {
// MM:SS 格式
return parts[0] * 60 + parts[1];
} else {
// HH:MM:SS 格式
return parts[0] * 3600 + parts[1] * 60 + parts[2];
}
}
// 将总秒数转换为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) {
// 使用更具体的选择器
const videoItems = document.querySelectorAll('.simple-base-item .stats .stat-item.duration');
let totalSeconds = 0;
let totalEpisodes = videoItems.length;
let validDurations = 0;
if (totalEpisodes === 0) {
console.log('未找到视频时长元素');
return {
duration: '0:00:00',
totalEpisodes: 0,
remainingEpisodes: 0
};
}
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();
const seconds = parseDuration(duration);
if (seconds > 0) {
totalSeconds += seconds;
remainingEpisodes++;
validDurations++;
console.log(`第${pageNum}集时长(有效): ${duration} -> ${seconds}秒`);
} else {
console.warn(`第${pageNum}集时长(无效): ${duration}`);
}
}
});
console.log(`有效时长数: ${validDurations}/${remainingEpisodes}`);
return {
duration: formatDuration(totalSeconds),
totalEpisodes: totalEpisodes,
remainingEpisodes: remainingEpisodes
};
}
// 创建控件
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 = `距离撒花还有${result.remainingEpisodes}集,` +
`总时长: ${result.duration} 🌸`;
}
resultBox.style.display = 'block';
}
// 初始化
function init() {
// 等待页面加载完成
const initInterval = setInterval(() => {
const durationElements = document.querySelectorAll('.simple-base-item .stats .stat-item.duration');
const titleContainer = document.querySelector('.video-info-title-inner');
if (durationElements.length > 0 && titleContainer) {
clearInterval(initInterval);
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);
}
}, 1000);
}
// 启动时添加延迟
setTimeout(() => {
console.log('脚本开始执行');
window.addEventListener('load', init);
}, 2000);
})();