您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为Javdb上评分高的影片增加热力图背景色,同时支持根据热力排序。
当前为
// ==UserScript== // @name Javdb 优质影片高亮 (排序与优化版) // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 为Javdb上评分高的影片增加热力图背景色,同时支持根据热力排序。 // @author JHT,黄页大嫖客(Modified by Gemini) // @match https://javdb.com/* // @grant GM_addStyle // @license GPLv3 // ==/UserScript== (function() { 'use strict'; // --- 配置项 --- const SCORE_THRESHOLD = 3.75; // 评分阈值,只有高于此分数的影片才会被着色 const MAX_RATED_BY = 200; // 用于归一化的最大评价人数,超过此值视为满热度贡献 /** * 根据综合热度值计算热力图颜色 (蓝 -> 绿 -> 红)。 * @param {number} heat - 一个 0 到 1 之间的值,代表热度。 * @returns {string} CSS rgba 颜色字符串,透明度固定为 50%。 */ function getHeatmapColor(heat) { let r = 0, g = 0, b = 0; const h = Math.min(Math.max(heat, 0), 1); if (h < 0.5) { const t = h * 2; g = Math.round(255 * t); b = Math.round(255 * (1 - t)); } else { const t = (h - 0.5) * 2; r = Math.round(255 * t); g = Math.round(255 * (1 - t)); } return `rgba(${r}, ${g}, ${b}, 0.5)`; } /** * 根据影片评分和评价人数计算综合热度值。 * @param {number} score - 影片评分。 * @param {number} ratedBy - 评价人数。 * @returns {number} 一个 0 到 1 之间的热度值。 */ function calculateHeat(score, ratedBy) { const scoreNormalized = score / 5; const scoreTransformed = Math.pow(scoreNormalized, 2); const ratedByNormalized = Math.min(ratedBy / MAX_RATED_BY, 1); return scoreTransformed * ratedByNormalized; } const scoreRegex = /([\d.]+)分, 由(\d+)人評價/; /** * 计算热度并将其存储在元素上,然后应用高亮。 * @param {HTMLElement} item - 影片的 item 元素。 */ function processAndHighlightItem(item) { if (item.dataset.highlighted) return; const scoreElement = item.querySelector('.score .value'); const anchorElement = item.querySelector('a'); item.dataset.heat = '0'; if (scoreElement && anchorElement) { const scoreMatch = scoreElement.textContent.trim().match(scoreRegex); if (scoreMatch) { const score = parseFloat(scoreMatch[1]); if (score >= SCORE_THRESHOLD) { const ratedBy = parseInt(scoreMatch[2], 10); const heat = calculateHeat(score, ratedBy); item.dataset.heat = heat; anchorElement.style.backgroundColor = getHeatmapColor(heat); } } } item.dataset.highlighted = 'true'; } /** * 在指定的根元素下查找并处理所有影片项。 * @param {HTMLElement} rootElement - 开始搜索 .item 的根元素。 */ function processItemsIn(rootElement) { const lists = rootElement.matches('.movie-list') ? [rootElement] : rootElement.querySelectorAll('.movie-list'); lists.forEach(list => { list.querySelectorAll('.item').forEach(processAndHighlightItem); }); } /** * 创建一个独立的、浮动的排序按钮。 */ function createIndependentSortButton() { if (!document.querySelector('.movie-list') || document.getElementById('sort-by-heat-btn-container')) { return; } const container = document.createElement('div'); container.id = 'sort-by-heat-btn-container'; const sortButton = document.createElement('a'); sortButton.textContent = '排序'; sortButton.className = 'button'; sortButton.addEventListener('click', (event) => { event.preventDefault(); sortItemsByHeat(); }); container.appendChild(sortButton); document.body.appendChild(container); GM_addStyle(` #sort-by-heat-btn-container { position: fixed; bottom: 27.2px; right: 0px; z-index: 9998; } #sort-by-heat-btn-container .button { width: 39.3px; height: 27.2px; font-size: 0.8rem; background-color: #fa6699; color: white; border: none; padding: 0; display: flex; align-items: center; justify-content: center; cursor: pointer; } `); } /** * 对页面上所有 movie-list 中的项目进行统一排序。 */ function sortItemsByHeat() { const containers = document.querySelectorAll('.movie-list'); if (containers.length === 0) return; let allItems = []; containers.forEach(container => { allItems = allItems.concat(Array.from(container.querySelectorAll('.item'))); }); allItems.sort((a, b) => { const heatA = parseFloat(a.dataset.heat || 0); const heatB = parseFloat(b.dataset.heat || 0); return heatB - heatA; }); const primaryContainer = containers[0]; const fragment = document.createDocumentFragment(); allItems.forEach(item => fragment.appendChild(item)); primaryContainer.innerHTML = ''; primaryContainer.appendChild(fragment); for (let i = 1; i < containers.length; i++) { containers[i].remove(); } // 动态获取顶部导航栏高度,以实现精确滚动,增强脚本对网站改版的适应性 const navBar = document.querySelector('.navbar.is-fixed-top'); const offset = navBar ? navBar.getBoundingClientRect().height : 53.45; // 若找不到则使用备用值 const elementTopPosition = primaryContainer.getBoundingClientRect().top + window.pageYOffset; const targetScrollPosition = elementTopPosition - offset; window.scrollTo({ top: targetScrollPosition, behavior: 'smooth' }); } /** * 脚本主函数/入口 */ function main() { try { // 1. 处理页面初次加载时已存在的项目 processItemsIn(document.body); // 2. 创建排序按钮 createIndependentSortButton(); // 3. 设置观察器,以处理后续动态加载的内容(如自动翻页) const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' && mutation.addedNodes.length) { // 每次有新内容加载时,都尝试创建按钮(如果尚不存在)并处理新项目 createIndependentSortButton(); mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { // 确保是元素节点 processItemsIn(node); } }); } } }); observer.observe(document.body, { childList: true, subtree: true }); } catch (error) { console.error("Javdb 高亮脚本出现错误:", error); } } // --- 脚本执行 --- // 等待DOM加载完毕后执行主函数,确保所有需要操作的元素都已就绪 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();