当前位置: 首页 > news >正文

tampermonkey油猴脚本, 动画疯评分显示增强脚本

  • 🎬 按需加载:在页面左下角提供一个控制面板,只有当您点击【获取评分】按钮时,脚本才会开始工作,避免了不必要的资源消耗。
  • ⭐ 自定义高亮:在获取评分前,会弹窗询问您希望高亮显示的分数阈值(默认≥4.5分),让您一眼就能找到符合您标准的优质动漫。
  • ⏯️ 高级控制:在评分获取过程中,您可以随时暂停继续停止当前任务,完全掌控脚本的行為。
  • 📊 进度显示:在处理过程中,会实时显示进度(例如 处理中: 15 / 24),让您对进度一目了然。
  • 🌐 通用支持:完美支持动画疯的首页所有动画列表页 (animeList.php) 和我的动画 三种不同的页面布局。
  • ⚡ 持久化缓存:已获取过的评分会被自动缓存24小时。在有效期内重复浏览,将直接从本地读取评分,实现秒级加载,并极大减少网络请求。
  • 🛡️ 防屏蔽策略:内置了随机延迟和伪装请求头等策略,模拟人类用户的浏览行为,有效避免触发网站的反爬虫机制。
  • 📍 UI位置:控制面板位于页面左下角,避免与网站右下角的官方弹窗重叠。
点击查看代码
// ==UserScript==
// @name         動畫瘋评分显示增强
// @namespace    http://tampermonkey.net/
// @version      4.2
// @description  支持暂停、继续、停止操作,UI移至左下角,按需获取评分并可自定义高亮阈值。
// @author       Your Name
// @match        https://ani.gamer.com.tw/*
// @connect      ani.gamer.com.tw
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==(function () {'use strict';// --- 全局配置与状态管理 ---// [配置项] 高亮的评分阈值,可通过弹窗动态修改let RATING_THRESHOLD = 4.5;// [配置项] 网络请求的基础延迟(毫秒),防止请求过快const BASE_DELAY_MS = 500;// [配置项] 在基础延迟上增加的随机延迟最大值,模拟人类行为const RANDOM_DELAY_MS = 1000;// [配置项] 本地缓存中存储评分的前缀,防止键名冲突const CACHE_PREFIX = 'anime_rating_';// [配置项] 缓存有效期(24小时),过期后会重新获取const CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000;// [状态变量] 存放待处理动漫卡片元素的任务队列let processingQueue = [];// [状态变量] 标记当前是否处于暂停状态let isPaused = false;// [状态变量] 标记当前是否已手动停止let isStopped = false;// [状态变量] 记录当前任务队列的总数,用于计算进度let totalQueueCount = 0;// --- UI 元素引用 ---// 将UI元素声明为全局变量,方便在不同函数中调用let controlContainer, startButton, pauseResumeButton, stopButton, progressIndicator;// --- 核心处理逻辑 ---/*** @description 任务队列的“引擎”,负责从队列中取出一个任务并处理。* 这是整个脚本实现暂停/继续的核心。*/function processQueue() {// 1. 检查状态:如果已暂停或已停止,则中断后续所有操作if (isPaused || isStopped) return;// 2. 检查队列是否为空:如果队列处理完毕,则重置UI并结束if (processingQueue.length === 0) {resetUI();return;}// 3. 更新进度条并从队列中取出第一个任务updateProgress();const card = processingQueue.shift(); // .shift()会移除并返回数组的第一个元素// 4. 处理这个取出的任务processAnimeCard(card);}/*** @description 处理单个动漫卡片的函数。* @param {HTMLElement} card - 需要处理的动漫卡片<a>元素。*/function processAnimeCard(card) {// 如果卡片已被处理过,则立即调度下一个任务if (card.classList.contains('rating-processed')) {setTimeout(processQueue, 50); // 用一个极短的延迟防止栈溢出return;}card.classList.add('rating-processed');const animeLink = card.href;// 基本的有效性检查if (!animeLink) { setTimeout(processQueue, 50); return; }const snMatch = animeLink.match(/sn=(\d+)/);if (!snMatch) { setTimeout(processQueue, 50); return; }const animeSN = snMatch[1];// 优先从缓存读取数据const cachedData = getFromCache(animeSN);if (cachedData) {injectRating(card, cachedData);// 即便从缓存读取,也加入一个随机延迟,让整体进度看起来更自然const delay = BASE_DELAY_MS / 2 + Math.random() * RANDOM_DELAY_MS / 2;// 关键:当前任务处理完后,调度下一个任务setTimeout(processQueue, delay);return;}// 如果缓存中没有,则发起网络请求GM_xmlhttpRequest({method: "GET",url: animeLink,headers: { "User-Agent": navigator.userAgent, "Referer": window.location.href },onload: function (response) {if (response.status >= 200 && response.status < 400) {const parser = new DOMParser();const doc = parser.parseFromString(response.responseText, 'text/html');const ratingElement = doc.querySelector('.score-overall-number');const rating = ratingElement ? parseFloat(ratingElement.textContent).toFixed(1) : 'N/A';injectRating(card, rating);saveToCache(animeSN, rating);} else {injectRating(card, 'Error');}// 关键:请求成功后,调度下一个任务const delay = BASE_DELAY_MS + Math.random() * RANDOM_DELAY_MS;setTimeout(processQueue, delay);},onerror: function () {injectRating(card, 'Error');// 关键:请求失败后,同样要调度下一个任务,确保队列能继续走下去const delay = BASE_DELAY_MS + Math.random() * RANDOM_DELAY_MS;setTimeout(processQueue, delay);}});}// --- UI 控制与事件处理 ---/*** @description 创建并初始化所有控制按钮和面板。*/function createControls() {// 创建主容器controlContainer = document.createElement('div');controlContainer.style.position = 'fixed';controlContainer.style.bottom = '20px';controlContainer.style.left = '20px'; // 移到左下角controlContainer.style.zIndex = '9999';controlContainer.style.display = 'flex';controlContainer.style.gap = '10px';controlContainer.style.alignItems = 'center';// 创建按钮的辅助函数,避免重复代码const createButton = (id, text, onClick) => {const button = document.createElement('button');button.id = id;button.textContent = text;// 定义通用样式button.style.padding = '8px 12px';button.style.fontSize = '14px';button.style.color = 'white';button.style.border = 'none';button.style.borderRadius = '5px';button.style.cursor = 'pointer';button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';button.addEventListener('click', onClick);return button;};// 创建各个按钮和指示器startButton = createButton('startBtn', '获取评分', promptAndFetch);startButton.style.backgroundColor = '#00a0d8';pauseResumeButton = createButton('pauseResumeBtn', '暂停', handlePauseResume);pauseResumeButton.style.backgroundColor = '#ffc107';stopButton = createButton('stopBtn', '停止', handleStop);stopButton.style.backgroundColor = '#dc3545';progressIndicator = document.createElement('span');progressIndicator.style.color = 'black';progressIndicator.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';progressIndicator.style.padding = '5px 10px';progressIndicator.style.borderRadius = '5px';progressIndicator.style.fontSize = '14px';// 将元素添加入容器,并最终添加入页面controlContainer.append(startButton, pauseResumeButton, stopButton, progressIndicator);document.body.appendChild(controlContainer);resetUI(); // 初始化UI到“待命”状态}/*** @description 重置UI到初始状态(只显示“获取评分”按钮)。*/function resetUI() {startButton.style.display = 'inline-block';pauseResumeButton.style.display = 'none';stopButton.style.display = 'none';progressIndicator.style.display = 'none';pauseResumeButton.textContent = '暂停'; // 确保暂停/继续按钮的文字状态被重置}/*** @description 设置UI到“处理中”状态。*/function setProcessingUI() {startButton.style.display = 'none';pauseResumeButton.style.display = 'inline-block';stopButton.style.display = 'inline-block';progressIndicator.style.display = 'inline-block';}/*** @description 更新进度指示器的文本。*/function updateProgress() {const processedCount = totalQueueCount - processingQueue.length;progressIndicator.textContent = `处理中: ${processedCount} / ${totalQueueCount}`;}/*** @description “获取评分”按钮的点击事件处理函数,负责弹出询问框。*/function promptAndFetch() {const userInput = prompt('需要高亮≥多少分的动漫?', RATING_THRESHOLD);if (userInput === null) return; // 用户点取消则中止const newThreshold = parseFloat(userInput);if (!isNaN(newThreshold)) {RATING_THRESHOLD = newThreshold; // 更新全局阈值}startProcessing(); // 开始处理流程}/*** @description 初始化任务队列并开始处理。*/function startProcessing() {// 重置状态变量isStopped = false;isPaused = false;// 查找所有未处理的卡片,并构建任务队列const animeCards = document.querySelectorAll('a.anime-card-block:not(.rating-processed), a.theme-list-main:not(.rating-processed)');processingQueue = Array.from(animeCards);totalQueueCount = processingQueue.length;if (totalQueueCount === 0) {alert('当前页面已无未获取评分的动漫。');return;}setProcessingUI(); // 切换UI到处理中状态processQueue(); // 启动队列引擎}/*** @description “暂停/继续”按钮的点击事件处理函数。*/function handlePauseResume() {isPaused = !isPaused; // 切换暂停状态if (isPaused) {pauseResumeButton.textContent = '继续';pauseResumeButton.style.backgroundColor = '#28a745'; // 绿色代表“继续”} else {pauseResumeButton.textContent = '暂停';pauseResumeButton.style.backgroundColor = '#ffc107'; // 黄色代表“暂停”processQueue(); // 关键:在“继续”时,需要手动调用一次processQueue来重启处理链条}}/*** @description “停止”按钮的点击事件处理函数。*/function handleStop() {isStopped = true;processingQueue = []; // 清空任务队列,中断所有后续操作resetUI(); // 将UI恢复到初始状态}// --- 辅助函数 (缓存, 注入DOM) ---/*** @description 将评分标签和高亮样式注入到动漫卡片上。* @param {HTMLElement} card - 目标卡片元素* @param {string} rating - 评分字符串 (e.g., "4.8" or "N/A")*/function injectRating(card, rating) {// 确定评分标签应该被注入到哪个元素。// 因为“所有动画”页的卡片结构不同,我们需要一个判断。// 如果是“所有动画”页的卡片(a.theme-list-main),目标是其内部的图片容器(div.theme-img-block)。// 否则,目标就是卡片本身(a.anime-card-block)。const injectionTarget = card.classList.contains('theme-list-main') ? card.querySelector('.theme-img-block') : card;if (!injectionTarget) return; // 如果找不到目标,则退出// 创建评分标签<div>元素const ratingDiv = document.createElement('div');// 设置绝对定位,使其可以浮动在卡片右上角ratingDiv.style.position = 'absolute';ratingDiv.style.top = '5px';ratingDiv.style.right = '5px';// 设置样式使其美观ratingDiv.style.padding = '2px 6px';ratingDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.75)';ratingDiv.style.color = 'white';ratingDiv.style.fontSize = '14px';ratingDiv.style.fontWeight = 'bold';ratingDiv.style.borderRadius = '4px';ratingDiv.style.zIndex = '10'; // 确保在顶层显示// 设置评分文本ratingDiv.textContent = `★ ${rating}`;// 检查评分是否达到高亮阈值const numericRating = parseFloat(rating);if (!isNaN(numericRating) && numericRating >= RATING_THRESHOLD) {// 确定高亮边框应该应用到哪个元素。// 对于“所有动画”页的卡片,我们希望高亮整个<li>,即<a>的父元素。// 对于首页卡片,直接高亮<a>元素即可。const highlightTarget = card.classList.contains('theme-list-main') ? card.parentElement : card;highlightTarget.style.outline = '3px solid #FFD700'; // 应用金色外边框ratingDiv.style.color = '#FFD700'; // 同时将评分文字也变为金色}// 为确保绝对定位生效,注入目标的position必须是relative, absolute, 或 fixed。injectionTarget.style.position = 'relative';// 将创建好的评分标签添加入目标元素injectionTarget.appendChild(ratingDiv);}/*** @description 将评分数据存入localStorage。* @param {string} key - 缓存键(通常是动漫的SN号)* @param {string} value - 要缓存的值(评分)*/function saveToCache(key, value) {const item = {value: value,timestamp: new Date().getTime() // 存入当前时间戳,用于判断是否过期};localStorage.setItem(CACHE_PREFIX + key, JSON.stringify(item));}/*** @description 从localStorage读取有效的缓存数据。* @param {string} key - 缓存键* @returns {string|null} - 如果找到有效缓存则返回值,否则返回null*/function getFromCache(key) {const itemStr = localStorage.getItem(CACHE_PREFIX + key);if (!itemStr) return null; // 如果不存在,返回nullconst item = JSON.parse(itemStr);// 检查缓存是否已过期if (new Date().getTime() - item.timestamp > CACHE_EXPIRATION_MS) {localStorage.removeItem(CACHE_PREFIX + key); // 如果过期,删除该缓存return null;}return item.value; // 返回有效的缓存值}// --- 脚本入口 ---/*** @description 当页面加载完成后,执行脚本的入口函数。* 这里只创建UI控件,等待用户交互。*/window.addEventListener('load', createControls);})();
http://www.hskmm.com/?act=detail&tid=26819

相关文章:

  • 9.29课后整理 - GENGAR
  • 深入解析:【QT】`QTextCursor::insertText()`中插入彩色文本
  • Java方法专题 - 动手动脑问题与实验总结
  • 2025年中盘点
  • 学习问题日记-3
  • 差分约束乘法改加减
  • 01-方法-课后作业
  • 应用程序io接口
  • 边缘数据库近期想法(2)
  • 方法-课后作业1
  • io软件的层次结构
  • 2025年- H57-Lc165--994.腐烂的橘子(图论,广搜)--Java版 - 教程
  • 3,信号与槽机制 - 教程
  • 课后思考及作业:方法
  • 2025国庆Day7
  • 月嫂面试题
  • 把握一个Makefile的脉络
  • 笔记 基础算法
  • P10068 [CCO 2023] Line Town
  • AI元人文:共识锚定与智慧剪枝——构建人机共生认知经济体的完善理论体系与实践路径
  • 出题系统
  • io控制方式
  • Java课后作业
  • 我 是 人 机
  • 28定律及其扩展衍生
  • 3516cv610在sample_aiisp上多创一路编码流,方法 - 详解
  • 2025氧化石墨烯、羧基化石墨烯、巯基化石墨烯、羟基化石墨烯、氨基化石墨烯、氮掺杂氧化石墨烯、氮掺杂石墨烯最新推荐、全面剖析优质厂商实力与选购要点
  • 2025-10-8模拟测验
  • QBXT2025S刷题 Day7
  • 【Python】调用C++