您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlights new anime releases on SubsPlease with color-coded episodes, thumbnail previews, enhanced download buttons, and direct search links to Nyaa and MyAnimeList
// ==UserScript== // @name SubsPlease New Show Tracker & Enhancer 🌟 // @namespace http://tampermonkey.net/ // @version 19.2 // @description Highlights new anime releases on SubsPlease with color-coded episodes, thumbnail previews, enhanced download buttons, and direct search links to Nyaa and MyAnimeList // @author Zotikus1001 // @license MIT // @match https://subsplease.org/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // Only run on main page const currentURL = window.location.href; if (currentURL.includes('/shows/') || currentURL.includes('/schedule/') || currentURL.includes('/xdcc/') || !currentURL.match(/https:\/\/subsplease\.org\/?$/)) { return; } // Styles const styles = ` /* Episode 01 Highlight */ .release-item.episode-01-highlight { border-radius: 8px !important; border: none !important; transform: scale(1.01) !important; transition: all 0.3s ease !important; position: relative !important; z-index: 1000 !important; margin: 3px !important; padding: 8px !important; } .release-item.episode-01-highlight:hover { transform: scale(1.03) !important; } .release-item.episode-01-highlight a { color: #000 !important; font-weight: bold !important; } .release-item.episode-01-highlight::before { display: none !important; } /* NEW badge added via JavaScript */ .new-episode-badge { position: absolute !important; top: -8px !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: 999999 !important; pointer-events: none !important; } /* Thumbnail overlay badge - the chosen one! */ .new-episode-thumbnail-badge { position: absolute !important; left: 8px !important; top: 8px !important; background: rgba(255, 71, 87, 0.95) !important; color: white !important; padding: 3px 8px !important; border-radius: 12px !important; font-size: 9px !important; font-weight: bold !important; border: 2px solid white !important; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3) !important; z-index: 999999 !important; pointer-events: none !important; animation: fade-in-out 2s ease-in-out infinite alternate !important; } /* Maintain positioning for not-interested shows with NEW badge */ .release-item.not-interested-show { position: relative !important; } /* Animation for the chosen badge */ @keyframes fade-in-out { 0% { opacity: 0.8; transform: scale(1); } 100% { opacity: 1; transform: scale(1.05); } } /* Subsequent Episodes */ .release-item.episode-02-highlight { border-radius: 6px !important; border: none !important; box-shadow: 0 0 8px currentColor !important; transform: scale(1.005) !important; transition: all 0.3s ease !important; z-index: 999 !important; margin: 2px !important; padding: 6px !important; opacity: 0.85 !important; } .release-item.episode-03-highlight { border-radius: 4px !important; border: none !important; box-shadow: 0 0 5px currentColor !important; transition: all 0.3s ease !important; z-index: 998 !important; margin: 2px !important; padding: 4px !important; opacity: 0.7 !important; } .release-item.episode-other-highlight { border-radius: 3px !important; border: none !important; box-shadow: 0 0 3px currentColor !important; transition: all 0.3s ease !important; z-index: 997 !important; margin: 2px !important; padding: 2px !important; opacity: 0.5 !important; } .release-item.episode-02-highlight a, .release-item.episode-03-highlight a, .release-item.episode-other-highlight a { color: #000 !important; font-weight: bold !important; } .release-item.episode-02-highlight:hover, .release-item.episode-03-highlight:hover, .release-item.episode-other-highlight:hover { transform: scale(1.02) !important; filter: brightness(1.1) !important; } /* Lower version styling */ .release-item.lower-version { opacity: 0.4 !important; filter: grayscale(0.6) !important; transform: scale(0.98) !important; border-style: dashed !important; } .release-item.lower-version::after { content: "⚠️ OLD" !important; position: absolute !important; top: 2px !important; left: 2px !important; background: rgba(255, 152, 0, 0.8) !important; color: white !important; padding: 1px 4px !important; border-radius: 3px !important; font-size: 8px !important; font-weight: bold !important; z-index: 1001 !important; } /* Dynamic thumbnail container */ .episode-thumbnail-container { float: left !important; margin-right: 12px !important; margin-top: 4px !important; margin-bottom: 4px !important; } .episode-thumbnail { max-width: 104px !important; max-height: 78px !important; object-fit: contain !important; border-radius: 4px !important; opacity: 0.8 !important; transition: all 0.3s ease !important; border: 1px solid rgba(255,255,255,0.3) !important; display: block !important; cursor: pointer !important; } .episode-thumbnail:hover { opacity: 1 !important; transform: scale(1.05) !important; border: 2px solid rgba(255,255,255,0.8) !important; box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important; } /* NO IMG placeholder */ .episode-thumbnail-placeholder { max-width: 104px !important; max-height: 78px !important; width: 58px !important; height: 78px !important; background: rgba(255,255,255,0.05) !important; border: 1px solid rgba(255,255,255,0.2) !important; border-radius: 4px !important; display: flex !important; align-items: center !important; justify-content: center !important; color: rgba(255,255,255,0.3) !important; font-size: 10px !important; font-weight: bold !important; transition: all 0.3s ease !important; } .episode-thumbnail-placeholder:hover { background: rgba(255,255,255,0.08) !important; border-color: rgba(255,255,255,0.4) !important; color: rgba(255,255,255,0.5) !important; } /* Dynamic container sizing */ .release-item.has-thumbnail { min-height: 90px !important; overflow: hidden !important; } .release-item.has-thumbnail::after { content: "" !important; display: table !important; clear: both !important; } /* Enhanced button styling - uniform for all buttons */ .badge-wrapper a, .badge-wrapper span { color: white !important; padding: 4px 8px !important; margin: 2px !important; border-radius: 4px !important; text-decoration: none !important; font-weight: bold !important; font-size: 11px !important; text-align: center !important; display: inline-block !important; vertical-align: middle !important; box-sizing: border-box !important; transition: all 0.2s ease !important; min-width: 50px !important; height: 24px !important; line-height: 16px !important; position: relative !important; } /* MMO-style quality colors - all with consistent sizing */ .badge-wrapper a[href*="480p"] { background: #28a745 !important; color: white !important; padding: 4px 4px 4px 22px !important; min-width: 60px !important; } .badge-wrapper a[href*="480p"]:hover { background: #218838 !important; transform: scale(1.05) !important; } .badge-wrapper a[href*="480p"]::before { content: "📱 " !important; position: absolute !important; left: 6px !important; } .badge-wrapper a[href*="720p"] { background: #007bff !important; color: white !important; padding: 4px 4px 4px 22px !important; min-width: 60px !important; } .badge-wrapper a[href*="720p"]:hover { background: #0056b3 !important; transform: scale(1.05) !important; } .badge-wrapper a[href*="720p"]::before { content: "🖥️ " !important; position: absolute !important; left: 6px !important; } .badge-wrapper a[href*="1080p"] { background: #6f42c1 !important; color: white !important; padding: 4px 4px 4px 22px !important; min-width: 65px !important; } .badge-wrapper a[href*="1080p"]:hover { background: #563d7c !important; transform: scale(1.05) !important; } .badge-wrapper a[href*="1080p"]::before { content: "🎬 " !important; position: absolute !important; left: 6px !important; } .badge-wrapper a[href*="xdcc"] { background: #6c757d !important; color: #adb5bd !important; font-weight: normal !important; opacity: 0.7 !important; padding: 4px 4px 4px 22px !important; min-width: 60px !important; } .badge-wrapper a[href*="xdcc"]:hover { background: #5a6268 !important; opacity: 0.9 !important; transform: scale(1.05) !important; } .badge-wrapper a[href*="xdcc"]::before { content: "📦 " !important; position: absolute !important; left: 6px !important; } /* Nyaa links */ .badge-wrapper a.nyaa-search-link { background: #e67e22 !important; color: white !important; padding: 4px 4px 4px 22px !important; min-width: 60px !important; } .badge-wrapper a.nyaa-search-link:hover { background: #d35400 !important; transform: scale(1.05) !important; } .badge-wrapper a.nyaa-search-link::before { content: "" !important; position: absolute !important; left: 6px !important; top: 50% !important; transform: translateY(-50%) !important; width: 14px !important; height: 14px !important; background-image: url('https://nyaa.si/static/favicon.png') !important; background-size: contain !important; background-repeat: no-repeat !important; background-position: center !important; } /* MAL links */ .badge-wrapper a.mal-search-link { background: #2e51a2 !important; color: white !important; padding: 4px 4px 4px 22px !important; min-width: 55px !important; } .badge-wrapper a.mal-search-link:hover { background: #1d439b !important; transform: scale(1.05) !important; } .badge-wrapper a.mal-search-link::before { content: "" !important; position: absolute !important; left: 6px !important; top: 50% !important; transform: translateY(-50%) !important; width: 14px !important; height: 14px !important; background-image: url('https://cdn.myanimelist.net/images/favicon.svg') !important; background-size: contain !important; background-repeat: no-repeat !important; background-position: center !important; } /* Watch list buttons */ .watch-list-controls { display: flex !important; gap: 4px !important; margin-top: 8px !important; opacity: 0 !important; transition: opacity 0.2s ease !important; } .release-item:hover .watch-list-controls { opacity: 1 !important; } .watch-list-btn { padding: 3px 6px !important; font-size: 9px !important; border: none !important; border-radius: 3px !important; cursor: pointer !important; color: white !important; font-weight: bold !important; transition: all 0.2s ease !important; white-space: nowrap !important; } .watch-list-btn:hover { transform: scale(1.05) !important; box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important; } .watch-btn-watching { background: #27ae60 !important; } .watch-btn-watching:hover { background: #229954 !important; } .watch-btn-considering { background: #f39c12 !important; } .watch-btn-considering:hover { background: #e67e22 !important; } .watch-btn-plan { background: #9b59b6 !important; } .watch-btn-plan:hover { background: #8e44ad !important; } .watch-btn-not-interested { background: #7f8c8d !important; } .watch-btn-not-interested:hover { background: #5d6d7e !important; } .watch-btn-remove { background: #e74c3c !important; } .watch-btn-remove:hover { background: #c0392b !important; } /* Thumbnail glow effects based on watch status */ .episode-thumbnail.status-watching, .episode-thumbnail-placeholder.status-watching { box-shadow: 0 0 12px #27ae60 !important; border: 2px solid #27ae60 !important; } .episode-thumbnail.status-considering, .episode-thumbnail-placeholder.status-considering { box-shadow: 0 0 12px #f39c12 !important; border: 2px solid #f39c12 !important; } .episode-thumbnail.status-plan, .episode-thumbnail-placeholder.status-plan { box-shadow: 0 0 12px #9b59b6 !important; border: 2px solid #9b59b6 !important; } /* Not interested - lower opacity */ .episode-thumbnail.status-not-interested, .episode-thumbnail-placeholder.status-not-interested { opacity: 0.3 !important; filter: grayscale(0.8) !important; } /* UI Elements */ .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: 100000 !important; font-family: Arial, sans-serif !important; } .found-list { position: fixed !important; top: 70px !important; right: 20px !important; width: 450px !important; max-height: 600px !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: 100001 !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: 100002 !important; font-family: Arial, sans-serif !important; border: 1px solid rgba(255,255,255,0.3) !important; transition: all 0.2s ease !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 preview image z-index */ .preview-image, [id*="preview"], [class*="preview"], [class*="tooltip"], [class*="hover"], img[src*="subsplease"], img[src*="preview"], .image-preview, .hover-image, img[data-preview], [data-preview-image] { z-index: 9999999 !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); } 100% { box-shadow: 0 0 25px rgba(0, 255, 136, 1), inset 0 0 15px rgba(0, 255, 255, 0.5); } } @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); } } `; let scanCount = 0; let foundEpisodes = []; let showsWithEp01 = new Set(); let debugMode = localStorage.getItem('subsplease-debug-mode') === 'true'; // Pleasant colors - reorganized to avoid similar colors const colors = [ '#ff7675', '#00b894', '#6c5ce7', '#fdcb6e', '#74b9ff', '#e17055', '#a29bfe', '#00cec9', '#ff6348', '#2ed573', '#5352ed', '#ffa502', '#ff6b9d', '#20bf6b', '#c44569', '#7bed9f', '#f0932b', '#70a1ff', '#eb4d4b', '#4b7bec', '#f8b500', '#48dbfb', '#778ca3', '#95afc0', '#ff9ff3', '#dda0dd', '#535c68', '#ffeaa7', '#fab1a0', '#81ecec', '#fd79a8', '#55a3ff' ]; let usedColors = new Map(); let showColors = new Map(); function getShowColor(showName, index) { if (showColors.has(showName)) { return showColors.get(showName); } let hash = 0; for (let i = 0; i < showName.length; i++) { hash = ((hash << 5) - hash) + showName.charCodeAt(i); } let colorIndex = Math.abs(hash) % colors.length; let selectedColor = colors[colorIndex]; const prevColor = usedColors.get(index - 1); const nextColor = usedColors.get(index + 1); let attempts = 0; while (attempts < colors.length && (isSimilarColor(selectedColor, prevColor) || isSimilarColor(selectedColor, nextColor))) { colorIndex = (colorIndex + 7) % colors.length; selectedColor = colors[colorIndex]; attempts++; } usedColors.set(index, selectedColor); showColors.set(showName, selectedColor); return selectedColor; } function getDimmedColor(baseColor, episodeNumber) { const hex = baseColor.replace('#', ''); const r = parseInt(hex.substr(0, 2), 16); const g = parseInt(hex.substr(2, 2), 16); const b = parseInt(hex.substr(4, 2), 16); const dimFactor = Math.pow(0.8, episodeNumber - 1); const newR = Math.round(r * dimFactor); const newG = Math.round(g * dimFactor); const newB = Math.round(b * dimFactor); return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`; } function isSimilarColor(color1, color2) { if (!color1 || !color2) return false; const colorFamilies = { red: ['#ff7675', '#ff6348', '#eb4d4b', '#ff6b9d', '#c44569', '#fd79a8'], blue: ['#74b9ff', '#70a1ff', '#4b7bec', '#48dbfb', '#55a3ff'], purple: ['#6c5ce7', '#a29bfe', '#5352ed', '#ff9ff3', '#dda0dd'], green: ['#00b894', '#00cec9', '#2ed573', '#20bf6b', '#7bed9f'], orange: ['#fdcb6e', '#e17055', '#ffa502', '#f0932b', '#f8b500', '#fab1a0'], yellow: ['#ffeaa7'], gray: ['#778ca3', '#95afc0', '#535c68'], cyan: ['#81ecec'] }; for (let family of Object.values(colorFamilies)) { if (family.includes(color1) && family.includes(color2)) { return true; } } return false; } function debugLog(...args) { if (debugMode) console.log(...args); } // Simple episode parsing function parseEpisode(text) { const match = text.match(/(.+?)\s*—\s*(\d{1,3})(v(\d+))?/i); if (match) { return { showName: match[1].trim(), episodeNumber: parseInt(match[2]), version: match[4] ? parseInt(match[4]) : 1, hasVersion: !!match[4] }; } return null; } // Generate MAL URL function generateMALURL(showName) { const encodedShow = encodeURIComponent(showName); return `https://myanimelist.net/anime.php?q=${encodedShow}&cat=anime`; } function generateNyaaURL(showName, episodeNumber) { const encodedShow = encodeURIComponent(showName).replace(/%20/g, '+'); const epNum = String(episodeNumber).padStart(2, '0'); const episodePattern = `%28${epNum}%7CE${epNum}%7CS01E${epNum}%29`; return `https://nyaa.si/?f=0&c=1_2&q=${encodedShow}+${episodePattern}`; } // Watch list management const STORAGE_KEY = 'subsplease-watchlist'; function getWatchList() { try { const data = localStorage.getItem(STORAGE_KEY); return data ? JSON.parse(data) : {}; } catch (e) { console.error('Error reading watch list:', e); return {}; } } function saveWatchList(watchList) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(watchList)); } catch (e) { console.error('Error saving watch list:', e); } } function setWatchStatus(showName, status) { const watchList = getWatchList(); if (status === 'remove') { delete watchList[showName]; } else { watchList[showName] = status; } saveWatchList(watchList); updateAllThumbnailGlows(); updateWatchButtons(showName); } function updateWatchButtons(showName) { const containers = document.querySelectorAll('#releases-table .release-item'); containers.forEach(container => { const link = container.querySelector('a'); if (!link) return; const text = link.textContent || ''; const episodeInfo = parseEpisode(text); if (!episodeInfo || episodeInfo.showName !== showName) return; // Remove existing watch controls const existingControls = container.querySelector('.watch-list-controls'); if (existingControls) { existingControls.remove(); } // Recreate them with updated status const badgeWrapper = container.querySelector('.badge-wrapper'); if (!badgeWrapper) return; const currentStatus = getWatchStatus(episodeInfo.showName); const watchControls = document.createElement('div'); watchControls.className = 'watch-list-controls'; const watchingBtn = document.createElement('button'); watchingBtn.className = 'watch-list-btn watch-btn-watching'; watchingBtn.textContent = '+ Watching'; watchingBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'watching'); }; const consideringBtn = document.createElement('button'); consideringBtn.className = 'watch-list-btn watch-btn-considering'; consideringBtn.textContent = '+ Considering'; consideringBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'considering'); }; const planBtn = document.createElement('button'); planBtn.className = 'watch-list-btn watch-btn-plan'; planBtn.textContent = '+ Plan To Watch'; planBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'plan'); }; const notInterestedBtn = document.createElement('button'); notInterestedBtn.className = 'watch-list-btn watch-btn-not-interested'; notInterestedBtn.textContent = '+ Not Interested'; notInterestedBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'not-interested'); }; watchControls.appendChild(watchingBtn); watchControls.appendChild(consideringBtn); watchControls.appendChild(planBtn); watchControls.appendChild(notInterestedBtn); // Only show remove button if show is already tracked if (currentStatus) { const removeBtn = document.createElement('button'); removeBtn.className = 'watch-list-btn watch-btn-remove'; removeBtn.textContent = '- Remove'; removeBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'remove'); }; watchControls.appendChild(removeBtn); } badgeWrapper.appendChild(watchControls); }); } function getWatchStatus(showName) { const watchList = getWatchList(); return watchList[showName] || null; } function updateAllThumbnailGlows() { const containers = document.querySelectorAll('#releases-table .release-item'); containers.forEach(container => { const link = container.querySelector('a'); if (!link) return; const text = link.textContent || ''; const episodeInfo = parseEpisode(text); if (!episodeInfo) return; const status = getWatchStatus(episodeInfo.showName); const thumbnail = container.querySelector('.episode-thumbnail, .episode-thumbnail-placeholder'); if (thumbnail) { thumbnail.classList.remove('status-watching', 'status-considering', 'status-plan', 'status-not-interested'); if (status === 'watching') { thumbnail.classList.add('status-watching'); } else if (status === 'considering') { thumbnail.classList.add('status-considering'); } else if (status === 'plan') { thumbnail.classList.add('status-plan'); } else if (status === 'not-interested') { thumbnail.classList.add('status-not-interested'); } } // Remove new show highlighting if marked as not interested if (status === 'not-interested') { container.classList.remove('episode-01-highlight', 'episode-02-highlight', 'episode-03-highlight', 'episode-other-highlight'); container.classList.add('not-interested-show'); container.style.removeProperty('background-color'); container.style.removeProperty('box-shadow'); } else { container.classList.remove('not-interested-show'); } }); } // Version management function manageVersions(containers) { const episodeMap = new Map(); containers.forEach(container => { const link = container.querySelector('a'); if (!link) return; const text = link.textContent || ''; const episodeInfo = parseEpisode(text); if (episodeInfo) { const key = `${episodeInfo.showName}-${episodeInfo.episodeNumber}`; if (!episodeMap.has(key)) { episodeMap.set(key, { containers: [], maxVersion: 0 }); } const entry = episodeMap.get(key); entry.containers.push({ container, episodeInfo }); entry.maxVersion = Math.max(entry.maxVersion, episodeInfo.version); } }); episodeMap.forEach((entry) => { if (entry.containers.length > 1) { entry.containers.forEach(({ container, episodeInfo }) => { if (episodeInfo.version < entry.maxVersion) { container.classList.add('lower-version'); } }); } }); } // Main highlighting function function highlightEpisodes() { scanCount++; debugLog(`🔍 === SCAN ${scanCount} ===`); usedColors.clear(); const containers = document.querySelectorAll('#releases-table .release-item'); if (containers.length === 0) return; debugLog(`🔍 Found ${containers.length} containers`); manageVersions(containers); const showsWithEp01Temp = new Set(); containers.forEach(container => { const link = container.querySelector('a'); if (!link) return; const text = link.textContent || ''; if (/^\d+p$/.test(text.trim()) || text.trim() === 'XDCC' || text.trim() === 'New!' || text.length < 5) { return; } if (container.classList.contains('lower-version')) return; const episodeInfo = parseEpisode(text); if (episodeInfo && (episodeInfo.episodeNumber === 0 || episodeInfo.episodeNumber === 1)) { showsWithEp01Temp.add(episodeInfo.showName); } }); let newCount = 0; let newEpisodes = []; let containerIndex = 0; containers.forEach(container => { const link = container.querySelector('a'); if (!link) return; const text = link.textContent || ''; if (/^\d+p$/.test(text.trim()) || text.trim() === 'XDCC' || text.trim() === 'New!' || text.length < 5) { return; } if (container.classList.contains('lower-version')) { return; } // Add thumbnail to ALL episodes const previewUrl = link.getAttribute('data-preview-image'); if (previewUrl && !container.querySelector('.episode-thumbnail') && !container.querySelector('.episode-thumbnail-placeholder')) { const thumbnailContainer = document.createElement('div'); thumbnailContainer.className = 'episode-thumbnail-container'; const isPlaceholder = previewUrl.includes('image-coming-soon-placeholder.png') || previewUrl === 'https://subsplease.org' || previewUrl === 'https://subsplease.org/'; if (isPlaceholder) { const placeholder = document.createElement('div'); placeholder.className = 'episode-thumbnail-placeholder'; placeholder.textContent = 'NO IMG YET'; thumbnailContainer.appendChild(placeholder); } else { const thumbnail = document.createElement('img'); thumbnail.src = previewUrl; thumbnail.className = 'episode-thumbnail'; thumbnail.alt = 'Episode thumbnail'; thumbnail.onerror = function() { thumbnailContainer.style.display = 'none'; }; Array.from(link.attributes).forEach(attr => { if (attr.name.startsWith('data-')) { thumbnail.setAttribute(attr.name, attr.value); } }); thumbnailContainer.appendChild(thumbnail); } container.insertBefore(thumbnailContainer, container.firstChild); container.classList.add('has-thumbnail'); } const episodeInfo = parseEpisode(text); // Add nyaa and MAL links for ALL episodes if (episodeInfo) { const badgeWrapper = container.querySelector('.badge-wrapper'); if (badgeWrapper) { // Add nyaa link if (!container.querySelector('.nyaa-search-link')) { const nyaaURL = generateNyaaURL(episodeInfo.showName, episodeInfo.episodeNumber); const nyaaLink = document.createElement('a'); nyaaLink.href = nyaaURL; nyaaLink.target = '_blank'; nyaaLink.className = 'nyaa-search-link'; nyaaLink.textContent = 'NYAA'; nyaaLink.title = `Search "${episodeInfo.showName} ${String(episodeInfo.episodeNumber).padStart(2, '0')}" on Nyaa`; badgeWrapper.appendChild(nyaaLink); } // Add MAL link if (!container.querySelector('.mal-search-link')) { const malURL = generateMALURL(episodeInfo.showName); const malLink = document.createElement('a'); malLink.href = malURL; malLink.target = '_blank'; malLink.className = 'mal-search-link'; malLink.textContent = 'MAL'; malLink.title = `Search "${episodeInfo.showName}" on MyAnimeList`; badgeWrapper.appendChild(malLink); } // Add watch list buttons for ALL episodes if (!container.querySelector('.watch-list-controls')) { const currentStatus = getWatchStatus(episodeInfo.showName); const watchControls = document.createElement('div'); watchControls.className = 'watch-list-controls'; const watchingBtn = document.createElement('button'); watchingBtn.className = 'watch-list-btn watch-btn-watching'; watchingBtn.textContent = '+ Watching'; watchingBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'watching'); }; const consideringBtn = document.createElement('button'); consideringBtn.className = 'watch-list-btn watch-btn-considering'; consideringBtn.textContent = '+ Considering'; consideringBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'considering'); }; const planBtn = document.createElement('button'); planBtn.className = 'watch-list-btn watch-btn-plan'; planBtn.textContent = '+ Plan To Watch'; planBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'plan'); }; const notInterestedBtn = document.createElement('button'); notInterestedBtn.className = 'watch-list-btn watch-btn-not-interested'; notInterestedBtn.textContent = '+ Not Interested'; notInterestedBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'not-interested'); }; watchControls.appendChild(watchingBtn); watchControls.appendChild(consideringBtn); watchControls.appendChild(planBtn); watchControls.appendChild(notInterestedBtn); // Only show remove button if show is already tracked if (currentStatus) { const removeBtn = document.createElement('button'); removeBtn.className = 'watch-list-btn watch-btn-remove'; removeBtn.textContent = '- Remove'; removeBtn.onclick = (e) => { e.preventDefault(); setWatchStatus(episodeInfo.showName, 'remove'); }; watchControls.appendChild(removeBtn); } badgeWrapper.appendChild(watchControls); } } } if (!episodeInfo) return; // Highlighting logic for episode 01s and subsequent episodes if ((episodeInfo.episodeNumber === 0 || episodeInfo.episodeNumber === 1) && !container.classList.contains('episode-01-highlight')) { container.classList.add('episode-01-highlight'); // Only count as new if not already in foundEpisodes if (!foundEpisodes.includes(text.trim())) { newCount++; newEpisodes.push(text.trim()); } showsWithEp01.add(episodeInfo.showName); const color = getShowColor(episodeInfo.showName, containerIndex); container.style.setProperty('background-color', color, 'important'); if (!container.querySelector('.new-episode-thumbnail-badge')) { const thumbnailBadge = document.createElement('div'); thumbnailBadge.className = 'new-episode-thumbnail-badge'; thumbnailBadge.textContent = 'NEW'; container.appendChild(thumbnailBadge); } debugLog(`✅ Episode ${episodeInfo.episodeNumber === 0 ? '00' : '01'}: "${text}" (${color})`); } else if (episodeInfo.episodeNumber > 1 && showsWithEp01Temp.has(episodeInfo.showName)) { let highlightClass = ''; if (episodeInfo.episodeNumber === 2) highlightClass = 'episode-02-highlight'; else if (episodeInfo.episodeNumber === 3) highlightClass = 'episode-03-highlight'; else if (episodeInfo.episodeNumber >= 4) highlightClass = 'episode-other-highlight'; if (highlightClass && !container.classList.contains(highlightClass)) { container.classList.add(highlightClass); const baseColor = getShowColor(episodeInfo.showName, containerIndex); const dimmedColor = getDimmedColor(baseColor, episodeInfo.episodeNumber); container.style.setProperty('background-color', dimmedColor, 'important'); container.style.setProperty('box-shadow', `0 0 ${highlightClass.includes('02') ? '8' : highlightClass.includes('03') ? '5' : '3'}px ${dimmedColor}`, 'important'); debugLog(`📺 Episode ${episodeInfo.episodeNumber}: "${text}" (${dimmedColor})`); } } containerIndex++; }); foundEpisodes = [...foundEpisodes, ...newEpisodes]; foundEpisodes = [...new Set(foundEpisodes)]; // Remove duplicates showsWithEp01 = new Set([...showsWithEp01, ...showsWithEp01Temp]); updateCounter(); updateList(); updateAllThumbnailGlows(); debugLog(`🔍 Scan ${scanCount}: Found ${newCount} new episode 01s (Total: ${foundEpisodes.length})`); } function updateCounter() { let counter = document.querySelector('.episode-counter'); if (!counter) { counter = document.createElement('div'); counter.className = 'episode-counter'; document.body.appendChild(counter); } counter.innerHTML = `🆕 New Shows: ${foundEpisodes.length}`; } function updateList() { let list = document.querySelector('.found-list'); if (!list) { list = document.createElement('div'); list.className = 'found-list'; document.body.appendChild(list); } const unique = [...new Set(foundEpisodes)]; const lowerCount = document.querySelectorAll('.release-item.lower-version').length; list.innerHTML = ` <h3>🆕 New Show's Found</h3> ${lowerCount > 0 ? `<div style="color: #ffa502; font-size: 11px; margin-bottom: 8px;">⚠️ ${lowerCount} older version(s) dimmed</div>` : ''} ${unique.length === 0 ? '<div>No episode 01s found yet...</div>' : unique.map(ep => `<div class="found-entry" data-episode="${ep}">🎯 ${ep}</div>`).join('')} `; const entries = list.querySelectorAll('.found-entry'); entries.forEach(entry => { entry.addEventListener('click', function() { const episodeName = this.getAttribute('data-episode'); scrollToEpisode(episodeName); }); }); } function scrollToEpisode(episodeName) { const containers = document.querySelectorAll('#releases-table .release-item.episode-01-highlight, #releases-table .release-item.episode-02-highlight, #releases-table .release-item.episode-03-highlight, #releases-table .release-item.episode-other-highlight'); for (let container of containers) { const link = container.querySelector('a'); if (link && link.textContent.trim() === episodeName) { container.scrollIntoView({ behavior: 'smooth', block: 'center' }); const originalBoxShadow = container.style.boxShadow; container.style.boxShadow = '0 0 30px #fff, 0 0 60px #00ffff, 0 0 90px #00ff88'; container.style.transform = 'scale(1.05)'; setTimeout(() => { container.style.boxShadow = originalBoxShadow; container.style.transform = 'scale(1.01)'; }, 800); break; } } } function createDebugToggle() { let toggle = document.querySelector('.debug-toggle'); if (!toggle) { toggle = document.createElement('div'); toggle.className = 'debug-toggle'; document.body.appendChild(toggle); toggle.addEventListener('click', function() { debugMode = !debugMode; localStorage.setItem('subsplease-debug-mode', debugMode.toString()); updateDebugToggle(); }); } updateDebugToggle(); } function updateDebugToggle() { const toggle = document.querySelector('.debug-toggle'); if (toggle) { toggle.textContent = debugMode ? '🔧 Debug: ON' : '🔧 Debug: OFF'; toggle.className = `debug-toggle ${debugMode ? 'debug-on' : ''}`; } } // Monitor API calls 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')) { xhr.addEventListener('load', function() { setTimeout(highlightEpisodes, 1000); }); } return originalOpen.apply(this, arguments); }; return xhr; }; // Initialize function init() { debugLog('🌟 SubsPlease New Show Tracker starting...'); const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); createDebugToggle(); setTimeout(highlightEpisodes, 1000); setTimeout(highlightEpisodes, 3000); setTimeout(updateAllThumbnailGlows, 3500); 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) { setTimeout(highlightEpisodes, 300); } }); const table = document.querySelector('#releases-table'); if (table) { observer.observe(table, { childList: true, subtree: true }); } setInterval(highlightEpisodes, 10000); debugLog('🌟 Tracker activated!'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('load', function() { setTimeout(highlightEpisodes, 2000); }); })();