// ==UserScript==
// @name SubsPlease New Show Tracker & Enhancer ✨
// @namespace http://tampermonkey.net/
// @version 17.0
// @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;
animation: neon-pulse 2s ease-in-out infinite alternate !important;
box-shadow: 0 0 15px rgba(0, 255, 136, 0.8), inset 0 0 10px rgba(0, 255, 255, 0.3) !important;
}
.release-item.episode-01-highlight:hover {
transform: scale(1.03) !important;
box-shadow: 0 0 25px rgba(0, 255, 255, 1), inset 0 0 15px rgba(0, 255, 136, 0.5) !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;
}
/* 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;
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;
}
.badge-wrapper a[href*="xdcc"]::before {
content: "📦 " !important;
position: absolute !important;
left: 6px !important;
}
/* Nyaa links - same styling pattern as other buttons */
.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;
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;
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;
}
/* 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(); // Track colors by position to avoid neighbors
let showColors = new Map(); // Store colors by show name for consistency
function getShowColor(showName, index) {
// If we already have a color for this show, return it
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);
}
// Get base color
let colorIndex = Math.abs(hash) % colors.length;
let selectedColor = colors[colorIndex];
// Check if previous or next colors are too similar
const prevColor = usedColors.get(index - 1);
const nextColor = usedColors.get(index + 1);
// If current color is too similar to neighbors, find a different one
let attempts = 0;
while (attempts < colors.length &&
(isSimilarColor(selectedColor, prevColor) || isSimilarColor(selectedColor, nextColor))) {
colorIndex = (colorIndex + 7) % colors.length; // Skip by 7 to get different color family
selectedColor = colors[colorIndex];
attempts++;
}
usedColors.set(index, selectedColor);
showColors.set(showName, selectedColor); // Store for this show
return selectedColor;
}
function getDimmedColor(baseColor, episodeNumber) {
// Convert hex to RGB
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);
// Calculate dimming factor (20% dimmer for each subsequent episode)
const dimFactor = Math.pow(0.8, episodeNumber - 1);
// Apply dimming
const newR = Math.round(r * dimFactor);
const newG = Math.round(g * dimFactor);
const newB = Math.round(b * dimFactor);
// Convert back to hex
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;
// Simple similarity check - compare color families
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}`;
}
// 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);
}
});
// Mark lower versions
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} ===`);
// Reset color tracking for fresh distribution
usedColors.clear();
// Keep showColors to maintain consistency across episodes of same show
const containers = document.querySelectorAll('#releases-table .release-item');
if (containers.length === 0) return;
debugLog(`🔍 Found ${containers.length} containers`);
// Handle versions first
manageVersions(containers);
// First, identify shows with episode 01
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 === 1) {
showsWithEp01Temp.add(episodeInfo.showName);
}
});
let newCount = 0;
let newEpisodes = [];
let containerIndex = 0; // Track position for color distribution
// Process all episodes
containers.forEach(container => {
const link = container.querySelector('a');
if (!link) return;
const text = link.textContent || '';
// Skip utility links
if (/^\d+p$/.test(text.trim()) || text.trim() === 'XDCC' || text.trim() === 'New!' || text.length < 5) {
return;
}
// Skip lower versions
if (container.classList.contains('lower-version')) {
return;
}
// Add thumbnail to ALL episodes/movies (moved outside episodeInfo check)
const previewUrl = link.getAttribute('data-preview-image');
if (previewUrl && !container.querySelector('.episode-thumbnail') && !container.querySelector('.episode-thumbnail-placeholder')) {
// Create thumbnail container
const thumbnailContainer = document.createElement('div');
thumbnailContainer.className = 'episode-thumbnail-container';
// Check if it's the placeholder image (either the full placeholder URL or just base domain)
const isPlaceholder = previewUrl.includes('image-coming-soon-placeholder.png') ||
previewUrl === 'https://subsplease.org' ||
previewUrl === 'https://subsplease.org/';
if (isPlaceholder) {
// Create custom NO IMG placeholder
const placeholder = document.createElement('div');
placeholder.className = 'episode-thumbnail-placeholder';
placeholder.textContent = 'NO IMG';
thumbnailContainer.appendChild(placeholder);
} else {
// Create regular thumbnail
const thumbnail = document.createElement('img');
thumbnail.src = previewUrl;
thumbnail.className = 'episode-thumbnail';
thumbnail.alt = 'Episode thumbnail';
thumbnail.onerror = function() {
thumbnailContainer.style.display = 'none';
};
// Copy all preview-related attributes from the link to the thumbnail
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
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);
}
}
}
if (!episodeInfo) return;
// Highlighting logic
if (episodeInfo.episodeNumber === 1 && !container.classList.contains('episode-01-highlight')) {
// Episode 01
container.classList.add('episode-01-highlight');
newCount++;
newEpisodes.push(text.trim());
showsWithEp01.add(episodeInfo.showName);
const color = getShowColor(episodeInfo.showName, containerIndex);
container.style.setProperty('background-color', color, 'important');
// Add NEW badge - simple without hover
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 01: "${text}" (${color})`);
} else if (episodeInfo.episodeNumber > 1 && showsWithEp01Temp.has(episodeInfo.showName)) {
// Subsequent episodes
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);
// Use the same base color as episode 01, but dimmed
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++; // Increment for next container
});
foundEpisodes = [...foundEpisodes, ...newEpisodes];
showsWithEp01 = new Set([...showsWithEp01, ...showsWithEp01Temp]);
updateCounter();
updateList();
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('')}
`;
// Add click handlers
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' });
// Flash effect
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 Final Working Highlighter starting...');
// Add styles
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
createDebugToggle();
// Initial scans
setTimeout(highlightEpisodes, 1000);
setTimeout(highlightEpisodes, 3000);
// Monitor 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) {
setTimeout(highlightEpisodes, 300);
}
});
const table = document.querySelector('#releases-table');
if (table) {
observer.observe(table, { childList: true, subtree: true });
}
setInterval(highlightEpisodes, 10000);
debugLog('🌟 Final Working Highlighter activated! 🎯');
}
// Start
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('load', function() {
setTimeout(highlightEpisodes, 2000);
});
})();