// ==UserScript== // @name B站♥视频评级 // @namespace http://tampermonkey.net/ // @version 1.13 // @description 计算B站视频的互动播放比并显示,点击可复制信息并随机显示酷炫或可爱浮动特效 // @author Zola // @match https://www.bilibili.com/video/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 工具函数:解析包含“万”或“亿”的数字 function parseCount(text) { if (!text) return 0; if (text.includes('万')) return parseFloat(text) * 10000; if (text.includes('亿')) return parseFloat(text) * 100000000; return parseInt(text.replace(/,/g, '')) || 0; } // 计算互动与播放量的比率 function calculateRatio(totalInteractions, viewCount) { return ((totalInteractions / viewCount) * 100).toFixed(2); } // 获取带颜色的评级HTML function getRating(ratio) { const r = parseFloat(ratio); const ratings = [ { min: 30, text: '满分视频', color: '#02c8d3' }, { min: 19, text: '好评如潮', color: 'green' }, { min: 16, text: '特别好评', color: 'limegreen' }, { min: 14, text: '多半好评', color: 'yellowgreen' }, { min: 8, text: '褒贬不一', color: 'orange' }, { min: 4, text: '多半差评', color: 'orangered' }, { min: 1, text: '差评如潮', color: 'red' }, { min: 0, text: '惨不忍睹', color: 'grey' } ]; const { text, color } = ratings.find(rating => r >= rating.min) || ratings[ratings.length - 1]; return `${text}`; } // 获取纯文本评级(无HTML) function getPlainRating(ratio) { const r = parseFloat(ratio); if (r > 30) return '满分视频'; if (r >= 19) return '好评如潮'; if (r >= 16) return '特别好评'; if (r >= 14) return '多半好评'; if (r >= 8) return '褒贬不一'; if (r >= 4) return '多半差评'; if (r >= 1) return '差评如潮'; return '惨不忍睹'; } // 创建并显示浮动特效(随机酷炫或可爱) function showCopyEffect(event) { const effect = document.createElement('div'); const isCool = Math.random() > 0.5; // 50%概率选择酷炫或可爱 // 基础样式 effect.style.position = 'absolute'; effect.style.left = `${event.pageX}px`; effect.style.top = `${event.pageY - 30}px`; effect.style.padding = '6px 12px'; effect.style.zIndex = '9999'; effect.style.pointerEvents = 'none'; effect.style.opacity = '1'; effect.style.transition = 'all 0.6s ease-out'; if (isCool) { // 酷炫风格 effect.textContent = 'Copied!'; effect.style.background = 'linear-gradient(45deg, #ff00cc, #3333ff)'; effect.style.color = '#fff'; effect.style.borderRadius = '8px'; effect.style.fontSize = '14px'; effect.style.fontFamily = 'monospace'; effect.style.boxShadow = '0 0 15px rgba(255, 0, 204, 0.8)'; effect.style.transform = 'translateY(0) rotate(5deg)'; setTimeout(() => { effect.style.opacity = '0'; effect.style.transform = 'translateY(-30px) rotate(-5deg)'; }, 50); } else { // 可爱风格 effect.textContent = '复制啦~'; effect.style.background = 'rgba(255, 182, 193, 0.9)'; effect.style.color = '#fff'; effect.style.border = '2px solid #ff69b4'; effect.style.borderRadius = '15px'; effect.style.fontSize = '14px'; effect.style.fontFamily = 'Comic Sans MS, cursive'; effect.style.transform = 'translateY(0) scale(1)'; setTimeout(() => { effect.style.opacity = '0'; effect.style.transform = 'translateY(-20px) scale(1.1)'; }, 50); } document.body.appendChild(effect); setTimeout(() => effect.remove(), 650); // 动画结束后移除 } // 绑定复制事件 function bindCopyEvent(element, displayText, ratio, viewCount) { if (element.hasAttribute('data-click-bound')) return; element.addEventListener('click', (event) => { event.stopPropagation(); const title = document.title; const url = `${window.location.origin}${window.location.pathname}`; const praiseRate = displayText.replace('好评:', ''); const plainRating = viewCount < 1000 ? '' : getPlainRating(ratio); const textToCopy = `【${title}】【${url}】 好评率: ${praiseRate} 好评分级: ${plainRating}`; navigator.clipboard.writeText(textToCopy).then(() => { showCopyEffect(event); }).catch(err => { console.error('复制失败:', err); alert('复制失败,请手动复制'); }); }); element.setAttribute('data-click-bound', 'true'); } // 创建工具栏元素 function createToolbarItem(className, title, content) { const wrapper = document.createElement('div'); wrapper.className = 'toolbar-left-item-wrap'; wrapper.setAttribute('data-v-1359e6fc', ''); const item = document.createElement('div'); item.className = `${className} video-toolbar-left-item`; item.setAttribute('data-v-1359e6fc', ''); item.setAttribute('title', title); item.innerHTML = `${content}`; wrapper.appendChild(item); return { wrapper, item }; } // 更新或添加比率和评级 function updateRatioDisplay() { const viewText = document.querySelector('.view-text')?.innerText.trim() || '0'; const likeText = document.querySelector('.video-like-info.video-toolbar-item-text')?.innerText.trim().replace(/,/g, '') || '0'; const coinText = document.querySelector('.video-coin-info.video-toolbar-item-text')?.innerText.trim().replace(/,/g, '') || '0'; const favText = document.querySelector('.video-fav-info.video-toolbar-item-text')?.innerText.trim().replace(/,/g, '') || '0'; const shareText = document.querySelector('.video-share-info-text')?.innerText.trim().replace(/,/g, '') || '0'; const viewCount = parseCount(viewText); const totalInteractions = parseCount(likeText) + parseCount(coinText) + parseCount(favText) + parseCount(shareText); if (isNaN(viewCount) || isNaN(totalInteractions)) return; const ratio = viewCount < 1000 ? 0 : calculateRatio(totalInteractions, viewCount); const displayText = viewCount < 1000 ? '播放不足' : `好评:${(ratio * 5).toFixed(1)}%`; const ratingText = viewCount < 1000 ? '' : getRating(ratio); const toolbarLeftMain = document.querySelector('.video-toolbar-left-main'); if (!toolbarLeftMain) return; let ratioElement = document.querySelector('.video-like-ratio'); let ratingElement = document.querySelector('.video-like-rating'); if (ratioElement) { ratioElement.querySelector('.video-like-ratio-info').innerText = displayText; } else { const { wrapper, item } = createToolbarItem('video-like-ratio', '互动播放比', displayText); toolbarLeftMain.appendChild(wrapper); ratioElement = item; } if (ratingElement) { ratingElement.querySelector('.video-like-rating-info').innerHTML = ratingText; } else { const { wrapper, item } = createToolbarItem('video-like-rating', '评价', ratingText); toolbarLeftMain.appendChild(wrapper); ratingElement = item; } bindCopyEvent(ratioElement, displayText, ratio, viewCount); bindCopyEvent(ratingElement, displayText, ratio, viewCount); } // 定期刷新显示 function startPeriodicRefresh() { setInterval(updateRatioDisplay, 3000); } // 初始化 window.addEventListener('load', startPeriodicRefresh); })();