您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Color-coded episode highlighting (different colors per show) with click-to-scroll! 🎯🌈
// ==UserScript== // @name SubsPlease Episode 01 Highlighter ✨ (PRECISE) // @namespace http://tampermonkey.net/ // @version 9.0 // @description Color-coded episode highlighting (different colors per show) with click-to-scroll! 🎯🌈 // @author Zotikus1001 // @license MIT // @match https://subsplease.org/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // 🚫 Only run on main page - exit immediately if on show pages const currentURL = window.location.href; if (currentURL.includes('/shows/') || currentURL.includes('/schedule/') || currentURL.includes('/xdcc/') || !currentURL.match(/https:\/\/subsplease\.org\/?$/)) { return; // Exit completely - no initialization, no elements, nothing } // 🎨 Dark-mode-friendly styles for highlighting episode 01s const styles = ` .episode-01-highlight { color: #000 !important; border-radius: 6px !important; border: 2px solid #00ffff !important; transform: scale(1.02) !important; transition: all 0.3s ease !important; position: relative !important; z-index: 1000 !important; margin: 2px !important; font-weight: bold !important; animation: neon-pulse 2s ease-in-out infinite alternate !important; } .episode-01-highlight:hover { transform: scale(1.05) !important; background: #00ffff !important; box-shadow: 0 0 25px rgba(0, 255, 255, 1), inset 0 0 15px rgba(0, 255, 136, 0.5) !important; border-color: #00ff88 !important; } .episode-01-highlight::before { content: "🆕 NEW!" !important; position: absolute !important; top: -12px !important; right: -8px !important; background: #ff4757 !important; color: white !important; padding: 2px 6px !important; border-radius: 8px !important; font-size: 9px !important; font-weight: bold !important; animation: bounce 1.5s infinite !important; box-shadow: 0 0 10px rgba(255, 71, 87, 0.8) !important; z-index: 1001 !important; white-space: nowrap !important; border: 1px solid #fff !important; } /* 📺 Episode 02 Highlight - Dimmer, different colors per show */ .episode-02-highlight { border-radius: 6px !important; border: 1px solid currentColor !important; box-shadow: 0 0 8px currentColor !important; transform: scale(1.01) !important; transition: all 0.3s ease !important; position: relative !important; z-index: 999 !important; margin: 2px !important; font-weight: bold !important; opacity: 0.7 !important; color: #000 !important; } /* 📺 Episode 03 Highlight - Even Dimmer */ .episode-03-highlight { border-radius: 4px !important; border: 1px solid currentColor !important; box-shadow: 0 0 5px currentColor !important; transform: scale(1.005) !important; transition: all 0.3s ease !important; position: relative !important; z-index: 998 !important; margin: 2px !important; font-weight: normal !important; opacity: 0.5 !important; color: #000 !important; } /* 📺 Episode 04+ Highlight - Very Subtle */ .episode-other-highlight { border-radius: 3px !important; border: 1px solid currentColor !important; box-shadow: 0 0 3px currentColor !important; transition: all 0.3s ease !important; position: relative !important; z-index: 997 !important; margin: 2px !important; font-weight: normal !important; opacity: 0.3 !important; color: #000 !important; } .episode-02-highlight:hover, .episode-03-highlight:hover, .episode-other-highlight:hover { transform: scale(1.02) !important; filter: brightness(1.1) !important; } @keyframes neon-pulse { 0% { box-shadow: 0 0 15px rgba(0, 255, 136, 0.8), inset 0 0 10px rgba(0, 255, 255, 0.3); background: #00ff88; } 100% { box-shadow: 0 0 25px rgba(0, 255, 136, 1), inset 0 0 15px rgba(0, 255, 255, 0.5); background: #00ffaa; } } @keyframes bounce { 0%, 100% { transform: translateY(0px) rotate(0deg); } 25% { transform: translateY(-2px) rotate(1deg); } 50% { transform: translateY(-4px) rotate(0deg); } 75% { transform: translateY(-2px) rotate(-1deg); } } .episode-counter { position: fixed !important; top: 20px !important; right: 20px !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; padding: 10px 15px !important; border-radius: 25px !important; font-weight: bold !important; box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important; z-index: 10000 !important; animation: counter-glow 2s infinite alternate !important; font-family: Arial, sans-serif !important; } @keyframes counter-glow { 0% { box-shadow: 0 4px 15px rgba(0,0,0,0.2); } 100% { box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4); } } .found-list { position: fixed !important; top: 70px !important; right: 20px !important; width: 400px !important; max-height: 500px !important; background: rgba(0,0,0,0.9) !important; color: white !important; padding: 15px !important; border-radius: 10px !important; font-size: 13px !important; overflow-y: auto !important; z-index: 10001 !important; font-family: Arial, sans-serif !important; border: 1px solid rgba(0, 255, 136, 0.3) !important; } .found-list h3 { margin: 0 0 12px 0 !important; color: #4ecdc4 !important; font-size: 14px !important; } .found-entry { margin-bottom: 5px !important; padding: 8px 10px !important; background: rgba(255,255,255,0.1) !important; border-radius: 5px !important; font-size: 12px !important; cursor: pointer !important; transition: all 0.2s ease !important; line-height: 1.3 !important; } .found-entry:hover { background: rgba(0, 255, 136, 0.3) !important; transform: translateX(3px) !important; box-shadow: 0 2px 8px rgba(0, 255, 136, 0.4) !important; } .debug-toggle { position: fixed !important; bottom: 20px !important; right: 20px !important; background: rgba(0,0,0,0.8) !important; color: white !important; padding: 8px 15px !important; border-radius: 20px !important; font-size: 11px !important; cursor: pointer !important; z-index: 10002 !important; font-family: Arial, sans-serif !important; border: 1px solid rgba(255,255,255,0.3) !important; transition: all 0.2s ease !important; white-space: nowrap !important; } .debug-toggle:hover { background: rgba(0, 255, 136, 0.2) !important; border-color: #00ff88 !important; transform: scale(1.05) !important; } .debug-toggle.debug-on { background: rgba(255, 71, 87, 0.8) !important; border-color: #ff4757 !important; } /* 🖼️ Fix image hover z-index to appear above episode names */ .preview-image, [id*="preview"], [class*="preview"], [class*="tooltip"], [class*="hover"], img[src*="subsplease"], img[src*="preview"], .image-preview, .hover-image { z-index: 10003 !important; } `; let scanCount = 0; let foundEpisodes = []; let showsWithEp01 = new Set(); // Track shows that have episode 01 let debugMode = false; // 🎨 Function to generate unique colors for different shows function getShowColor(showName) { // Improved hash function for better distribution let hash = 0; for (let i = 0; i < showName.length; i++) { const char = showName.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } // More distinct color palette - very different colors that are easy to distinguish const colors = [ '#ff4757', // Red '#3742fa', // Blue '#2ed573', // Green '#ffa502', // Orange '#9c88ff', // Purple '#00d2d3', // Cyan '#ff6348', // Coral '#dda0dd', // Plum '#ffb8b8', // Pink '#78e08f', // Light Green '#60a3bc', // Steel Blue '#f8b500', // Amber '#e74c3c', // Crimson '#8e44ad', // Dark Purple '#16a085', // Teal '#f39c12', // Dark Orange '#2980b9', // Strong Blue '#27ae60', // Emerald '#e67e22', // Carrot '#c0392b', // Dark Red '#9b59b6', // Amethyst '#1abc9c', // Turquoise '#f1c40f', // Yellow '#34495e', // Dark Gray '#95a5a6', // Silver ]; // Add secondary hash to reduce collisions let secondHash = 0; for (let i = 0; i < showName.length; i++) { secondHash += showName.charCodeAt(i) * (i + 1); } const combinedHash = Math.abs(hash + secondHash); return colors[combinedHash % colors.length]; } // 🔧 Debug logging function function debugLog(...args) { if (debugMode) { console.log(...args); } } // 🔧 Create debug toggle button function createDebugToggle() { let toggle = document.querySelector('.debug-toggle'); if (!toggle) { toggle = document.createElement('div'); toggle.className = 'debug-toggle'; toggle.title = 'Toggle console logging for SubsPlease Episode 01 Highlighter script'; document.body.appendChild(toggle); toggle.addEventListener('click', function() { debugMode = !debugMode; updateDebugToggle(); // Show user-friendly message about what was toggled if (debugMode) { console.log('🔧 SubsPlease Episode 01 Highlighter: Console logging ENABLED'); console.log('📝 You will now see detailed scan information in the console'); } else { console.log('🔧 SubsPlease Episode 01 Highlighter: Console logging DISABLED'); console.log('🔇 Script will run silently (no console spam)'); } }); } updateDebugToggle(); } // 🔧 Update debug toggle appearance function updateDebugToggle() { const toggle = document.querySelector('.debug-toggle'); if (toggle) { toggle.textContent = debugMode ? '🔧 Script Debug: ON' : '🔧 Script Debug: OFF'; toggle.className = `debug-toggle ${debugMode ? 'debug-on' : ''}`; toggle.title = debugMode ? 'Click to disable console logging for Episode 01 Highlighter' : 'Click to enable console logging for Episode 01 Highlighter'; } } // 📊 Function to update episode counter function updateEpisodeCounter(count) { let counter = document.querySelector('.episode-counter'); if (!counter) { counter = document.createElement('div'); counter.className = 'episode-counter'; document.body.appendChild(counter); } // Only count episode 01s for the counter const ep01Count = foundEpisodes.length; counter.innerHTML = `🆕 Episode 01s: ${ep01Count}`; } // 📋 Function to update found episodes list function updateFoundList(episodes) { let list = document.querySelector('.found-list'); if (!list) { list = document.createElement('div'); list.className = 'found-list'; document.body.appendChild(list); } const uniqueEpisodes = [...new Set(episodes)]; // Remove duplicates list.innerHTML = ` <h3>🆕 Episode 01s Found</h3> ${uniqueEpisodes.length === 0 ? '<div>No episode 01s found yet...</div>' : uniqueEpisodes.map((ep, index) => `<div class="found-entry" data-episode="${ep}">🎯 ${ep}</div>`).join('')} `; // 🎯 Add click handlers to scroll to episodes const entries = list.querySelectorAll('.found-entry'); entries.forEach(entry => { entry.addEventListener('click', function() { const episodeName = this.getAttribute('data-episode'); scrollToEpisode(episodeName); }); }); } // 📍 Function to scroll to a specific episode function scrollToEpisode(episodeName) { debugLog(`🎯 Scrolling to episode: "${episodeName}"`); // Find the episode link that matches the name (any highlight type) const episodeLinks = document.querySelectorAll('#releases-table a.episode-01-highlight, #releases-table a.episode-02-highlight, #releases-table a.episode-03-highlight, #releases-table a.episode-other-highlight'); for (let link of episodeLinks) { const linkText = link.textContent.trim(); if (linkText === episodeName) { // Scroll to the element with smooth animation link.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); // Add temporary highlight flash flashElement(link); debugLog(`✅ Scrolled to: "${episodeName}"`); return; } } debugLog(`❌ Episode not found: "${episodeName}"`); } // ✨ Function to flash highlight an element function flashElement(element) { const originalBoxShadow = element.style.boxShadow; // Add intense flash element.style.boxShadow = '0 0 30px #fff, 0 0 60px #00ffff, 0 0 90px #00ff88'; element.style.transform = 'scale(1.1)'; // Return to normal after animation setTimeout(() => { element.style.boxShadow = originalBoxShadow; element.style.transform = 'scale(1.02)'; }, 800); } // 🎯 ENHANCED function to highlight episode 01s AND subsequent episodes of same shows function highlightEpisode01s() { scanCount++; debugLog(`🔍 === ENHANCED SCAN ${scanCount} ===`); let newEpisode01Count = 0; let newFoundEpisodes = []; let newShowsWithEp01 = new Set(); // 🎯 Get all episode title links const episodeTitleLinks = document.querySelectorAll('#releases-table a'); debugLog(`🔍 Found ${episodeTitleLinks.length} episode title links`); // 📍 FIRST PASS: Find episode 01s and track show names episodeTitleLinks.forEach((link, index) => { const text = link.textContent || link.innerText || ''; // Skip quality links and short text if (/^\d+p$/.test(text.trim()) || text.trim() === 'XDCC' || text.trim() === 'New!' || text.length < 5) { return; } // Look for episode 01 patterns const episode01Patterns = [ /—\s*01(?:\s|$)/i, // "— 01" with em dash (main pattern) /-\s*01(?:\s|$)/i, // "- 01" with regular dash /\s01(?:\s|$)/i, // " 01" at end or followed by space ]; const matchedPattern = episode01Patterns.find(pattern => pattern.test(text)); if (matchedPattern && !link.classList.contains('episode-01-highlight')) { link.classList.add('episode-01-highlight'); newEpisode01Count++; newFoundEpisodes.push(text.trim()); // Extract show name (everything before the episode number) const showName = text.replace(/\s*[—-]\s*01.*$/i, '').trim(); newShowsWithEp01.add(showName); showsWithEp01.add(showName); // Apply show-specific color for episode 01 too const showColor = getShowColor(showName); link.style.setProperty('background-color', showColor, 'important'); link.style.setProperty('border-color', '#00ffff', 'important'); // Keep cyan border for episode 01s link.style.setProperty('box-shadow', `0 0 15px ${showColor}, inset 0 0 10px rgba(0, 255, 255, 0.3)`, 'important'); debugLog(`🆕 FOUND Episode 01: "${text}" (Show: "${showName}", Color: ${showColor})`); // Add click handler link.addEventListener('click', function() { debugLog('🆕 Clicked on Episode 01:', text); }); } }); // 📍 SECOND PASS: Find subsequent episodes for shows with episode 01 let subsequentEpisodesCount = 0; episodeTitleLinks.forEach((link, index) => { const text = link.textContent || link.innerText || ''; // Skip if already highlighted or is quality/short text if (link.classList.contains('episode-01-highlight') || link.classList.contains('episode-02-highlight') || link.classList.contains('episode-03-highlight') || link.classList.contains('episode-other-highlight') || /^\d+p$/.test(text.trim()) || text.trim() === 'XDCC' || text.trim() === 'New!' || text.length < 5) { return; } // Look for other episode patterns (02, 03, 04, etc.) const otherEpisodePattern = /(.+?)\s*[—-]\s*(\d{2,3})(?:\s|$)/i; const match = text.match(otherEpisodePattern); if (match) { const showName = match[1].trim(); const episodeNum = match[2]; // Check if this show has episode 01 present if (showsWithEp01.has(showName)) { const epNum = parseInt(episodeNum); let highlightClass = ''; if (epNum === 2) { highlightClass = 'episode-02-highlight'; } else if (epNum === 3) { highlightClass = 'episode-03-highlight'; } else if (epNum >= 4) { highlightClass = 'episode-other-highlight'; } if (highlightClass) { link.classList.add(highlightClass); // Apply show-specific color const showColor = getShowColor(showName); link.style.setProperty('background-color', showColor, 'important'); link.style.setProperty('border-color', showColor, 'important'); link.style.setProperty('box-shadow', `0 0 ${highlightClass.includes('02') ? '8' : highlightClass.includes('03') ? '5' : '3'}px ${showColor}`, 'important'); subsequentEpisodesCount++; debugLog(`📺 FOUND Episode ${episodeNum}: "${text}" (Show: "${showName}", Color: ${showColor})`); // Add click handler link.addEventListener('click', function() { debugLog(`📺 Clicked on Episode ${episodeNum}:`, text); }); } } } }); // Update found episodes list (only episode 01s) foundEpisodes = [...foundEpisodes, ...newFoundEpisodes]; updateFoundList(foundEpisodes); updateEpisodeCounter(foundEpisodes.length); debugLog(`🔍 Scan ${scanCount}: Found ${newEpisode01Count} NEW episode 01s, ${subsequentEpisodesCount} subsequent episodes (Total episode 01s: ${foundEpisodes.length})`); if (newFoundEpisodes.length > 0) { debugLog('🆕 New Episode 01s found:', newFoundEpisodes); } } // 🕵️ Monitor XHR requests to see when data loads const originalXHR = window.XMLHttpRequest; window.XMLHttpRequest = function() { const xhr = new originalXHR(); const originalOpen = xhr.open; xhr.open = function(method, url) { if (url.includes('api/?f=latest')) { debugLog('🌐 Latest releases API call detected'); xhr.addEventListener('load', function() { debugLog('🌐 Latest releases API response received - scanning in 1 second...'); setTimeout(highlightEpisode01s, 1000); }); } return originalOpen.apply(this, arguments); }; return xhr; }; // 🚀 Initialize the script function init() { debugLog('🌟 SubsPlease Episode 01 Highlighter PRECISE VERSION starting...'); // Add styles to the page const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); // Create debug toggle createDebugToggle(); // Initial scans with delays to catch dynamic content setTimeout(highlightEpisode01s, 1000); setTimeout(highlightEpisode01s, 3000); setTimeout(highlightEpisode01s, 5000); // 👀 MutationObserver for table changes const observer = new MutationObserver(function(mutations) { let tableChanged = false; mutations.forEach(function(mutation) { if (mutation.target.id === 'releases-table' || mutation.target.closest('#releases-table')) { tableChanged = true; } }); if (tableChanged) { debugLog('🔄 Table content changed - scanning...'); setTimeout(highlightEpisode01s, 300); } }); // Observe the releases table specifically const table = document.querySelector('#releases-table'); if (table) { observer.observe(table, { childList: true, subtree: true }); debugLog('📊 Table monitoring enabled'); } // 🔄 Periodic scanning (less frequent) setInterval(highlightEpisode01s, 10000); // 🎯 Monitor "See more" button function monitorSeeMoreButton() { const seeMoreButton = document.querySelector('#latest-load-more'); if (seeMoreButton && !seeMoreButton.hasAttribute('data-monitored')) { seeMoreButton.setAttribute('data-monitored', 'true'); seeMoreButton.addEventListener('click', function() { debugLog('🔄 "See more" clicked - will scan in 2 seconds...'); setTimeout(highlightEpisode01s, 2000); }); debugLog('📱 "See more" button monitoring enabled'); } } setTimeout(monitorSeeMoreButton, 1000); debugLog('🌟 SubsPlease Episode 01 Highlighter PRECISE VERSION activated!'); } // 🎬 Start the script if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('load', function() { setTimeout(highlightEpisode01s, 2000); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址