// ==UserScript==
// @name 1337x - Combined Enhancements 2025 (v11) - Configurable
// @namespace http://tampermonkey.net/
// @version 2025.11
// @description Adds a column with torrent and magnet links, extends titles, adds images, full width site with configurable settings
// @author sharmanhall
// @contributor darkred, NotNeo, barn852, French Bond
// @match *://*.1337x.to/*
// @match *://*.1337x.ws/*
// @match *://*.1337x.eu/*
// @match *://*.1337x.se/*
// @match *://*.1337x.is/*
// @match *://*.1337x.gd/*
// @match *://*.x1337x.cc/*
// @match *://*.1337x.st/*
// @match *://*.x1337x.ws/*
// @match *://*.x1337x.eu/*
// @match *://*.x1337x.se/*
// @match http://l337xdarkkaqfwzntnfk5bmoaroivtl6xsbatabvlb52umg6v3ch44yd.onion/*
// @match https://www.1337x.to/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=1337x.to
// ==/UserScript==
//
// Thanks to:
// - French Bond: modified original script taken from https://greasyfork.org/en/scripts/479974-1337x-ux-enhancement
// - darkred: modified original script taken from https://greasyfork.org/en/scripts/479974-1337x-ux-enhancement
// - NotNeo: most of the CSS used is taken from this script: https://greasyfork.org/en/scripts/373230-1337x-magnet-torrent-links-everywhere .
// - barn852 for his contribution here: https://greasyfork.org/en/scripts/420754-1337x-torrent-and-magnet-links/discussions/96026
//
// Official mirrors list: https://1337x.to/about
//
// @downloadURL https://update.greasyfork.org/scripts/483602/1337x%20-%20Combined%20Enhancements%202024.user.js
// @updateURL https://update.greasyfork.org/scripts/483602/1337x%20-%20Combined%20Enhancements%202024.meta.js
// ==/UserScript==
(function() {
'use strict';
// Configuration object
let config = {
showThumbnails: GM_getValue('showThumbnails', true),
showExtraColumn: GM_getValue('showExtraColumn', true), //shows column with magnet/torrent links
fullWidthSite: GM_getValue('fullWidthSite', true),
visibleImages: GM_getValue('visibleImages', 4),
queueFetchDelay: GM_getValue('queueFetchDelay', 0), //default value is 0ms between fetch requests
maxRetries: GM_getValue('maxRetries', 1) //default is 2 max retries per queue request
};
let extraColumnAdded = false;
// CSS for the settings menu
const settingsCSS = `
.list-button-magnet > i.flaticon-magnet {
font-size: 13px;
color: #da3a04
}
.list-button-dl > i.flaticon-torrent-download {
font-size: 13px;
color: #89ad19;
}
table.table-list td.dl-buttons {
border-left: 1px solid #f6f6f6;
border-right: 1px solid #c0c0c0;
padding-left: 2.5px;
padding-right: 2.5px;
text-align: center !important;
position: relative;
display: table-cell !important;
width: 6%;
}
td.dl-buttons > a,
td.dl-buttons > a:hover,
td.dl-buttons > a:visited,
td.dl-buttons > a:link,
td.dl-buttons > a:active {
color: inherit;
text-decoration: none;
cursor: pointer;
display: inline-block !important;
margin: 0 2px;
}
table.table-list td.coll-1b {
border-right: 1px solid silver;
}
.table-list > thead > tr > th:nth-child(2),
.table-list > thead > tr > td:nth-child(2) {
text-align: center;
}
#x1337-settings-wrapper {
font-family: 'Open Sans', sans-serif;
background-color: #1d1d1d;
color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(241, 78, 19, 0.5);
position: fixed;
top: 20px;
right: -300px; /* Start off-screen */
width: 300px;
transition: right 0.3s ease;
z-index: 9999;
}
#x1337-settings-toggle {
position: absolute;
left: -30px;
top: 0;
width: 30px;
height: 30px;
background-color: #F14E13;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
box-shadow: -2px 0 5px rgba(0,0,0,0.2);
cursor: pointer;
text-align: center;
line-height: 30px;
}
#x1337-settings-content {
padding: 15px;
}
#x1337-settings-content h3 {
color: #F14E13;
border-bottom: 1px solid #F14E13;
padding-bottom: 10px;
margin-bottom: 15px;
}
.x1337-option {
margin-bottom: 15px;
color: #fff;
}
.x1337-option label {
display: flex;
align-items: center;
cursor: pointer;
color: #fff;
}
.x1337-option input[type="checkbox"] {
margin-right: 10px;
}
.x1337-option input[type="number"],
.x1337-option input[type="text"] {
background-color: #2d2d2d;
border: 1px solid #F14E13;
color: #fff;
padding: 5px;
border-radius: 3px;
}
#x1337-save-settings {
background-color: #F14E13;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 3px;
cursor: pointer;
transition: background-color 0.3s;
box-shadow: 0 0 10px rgba(241, 78, 19, 0.5);
}
#x1337-save-settings:hover {
background-color: #ff6a3c;
}
/* Custom radio buttons */
.x1337-option input[type="radio"] {
display: none;
}
.x1337-option input[type="radio"] + label:before {
content: '';
display: inline-block;
width: 16px;
height: 16px;
margin-right: 10px;
border: 2px solid #F14E13;
border-radius: 50%;
background-color: #2d2d2d;
}
.x1337-option input[type="radio"]:checked + label:before {
background-color: #F14E13;
box-shadow: inset 0 0 0 3px #2d2d2d;
}
/* Ensure text is always white for better readability */
#x1337-settings-wrapper,
#x1337-settings-wrapper * {
color: #fff;
}
#x1337-popup {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background-color: #F14E13;
color: #fff;
padding: 10px 20px;
border-radius: 5px;
z-index: 10000;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
`;
// Add settings menu HTML
const settingsHTML = `
<script>
</script>
<div id="x1337-settings-wrapper">
<div id="x1337-settings-toggle">⚙️</div>
<div id="x1337-settings-content">
<h3>1337x Enhancements Settings</h3>
<div class="x1337-option">
<label>
<input type="checkbox" id="x1337-show-thumbnails" ${config.showThumbnails ? 'checked' : ''}>
Show Thumbnails
</label>
<div class="x1337-sub-option" ${config.showThumbnails ? '' : 'style="display:none;"'}>
<label>
Visible Images:
<input type="number" id="x1337-visible-images" value="${config.visibleImages}" min="1" max="10">
</label>
<label>
Queue Fetch Delay (ms):
<input type="number" id="x1337-queue-fetch-delay" value="${config.queueFetchDelay}" min="50" max="5000">
</label>
<label>
Max Fetch Retries:
<input type="number" id="x1337-max-retries" value="${config.maxRetries}" min="0" max="10">
</label>
</div>
</div>
<div class="x1337-option">
<label>
<input type="checkbox" id="x1337-show-magnet-column" ${config.showExtraColumn ? 'checked' : ''}>
Show Magnet URL Column
</label>
</div>
<div class="x1337-option">
<label>
<input type="checkbox" id="x1337-full-width-site" ${config.fullWidthSite ? 'checked' : ''}>
Full Width Site
</label>
</div>
<button id="x1337-save-settings">Save Settings</button><br>
<small>v2025.11 | by sharmanhall</small>
</div>
</div>
`;
function appendColumn() {
if (!config.showExtraColumn || extraColumnAdded) return;
const allTables = document.querySelectorAll('.table-list-wrap');
const isSeries = window.location.href.includes('/series/');
const title = 'ml dl';
allTables.forEach((table) => {
const headersCellsInitial = table.querySelectorAll(`.table-list > thead > tr:not(.blank) > th:nth-child(1),
.table-list > tbody > tr:not(.blank) > td:nth-child(1)`);
headersCellsInitial.forEach((cell, index) => {
if (index === 0 && !isSeries) {
cell.insertAdjacentHTML('afterend', `<th class="coll-1b">` + title + `</th>`);
} else {
// Find the correct link (the second one, which is the title link)
let titleLink = cell.querySelectorAll('a')[1];
let href = titleLink ? titleLink.href : '';
cell.insertAdjacentHTML('afterend', `
<td class="coll-1b dl-buttons">
<a class="list-button-magnet" href="javascript:void(0)" data-href="${href}" title="Magnet link">
<i class="flaticon-magnet"></i>
</a>
<a class="list-button-dl" href="javascript:void(0)" data-href="${href}" title="Torrent download">
<i class="flaticon-torrent-download"></i>
</a>
</td>
`);
}
});
extraColumnAdded = true;
});
addClickListeners(document.querySelectorAll('.list-button-magnet'), 'ml');
addClickListeners(document.querySelectorAll('.list-button-dl'), 'dl');
}
function handleDownloadClick(event) {
event.preventDefault();
const button = event.currentTarget;
console.log('Clicked button:', button);
let href = button.getAttribute('data-href');
console.log('Data-href attribute:', href);
if (!href) {
console.error('No data-href attribute found on the button');
showPopup('Error: No link found');
return;
}
// Prepend the base URL if it's a relative URL
if (href.startsWith('/')) {
href = 'https://1337x.to' + href;
}
const ismagnet = button.classList.contains('list-button-magnet');
console.log('Is magnet link:', ismagnet);
//fetchContent(href, (doc) => {
queuefetchContent(href, (doc) => {
console.log('Fetched content, searching for link');
showPopup('Fetched content, searching for link');
let link = ismagnet ?
doc.querySelector("a[href^='magnet:']") :
doc.querySelector("a[href*='itorrents.org/torrent/']");
if (link) {
console.log('Found link:', link.href);
showPopup('found link:', link.href);
button.href = link.href;
button.removeEventListener('click', handleDownloadClick);
console.log('Triggering click on button');
button.click();
} else {
console.error('Download link not found in fetched content');
console.log('Fetched HTML:', doc.documentElement.outerHTML);
showPopup('Download link not found');
}
}, 5000, 2); // Increased retry delay to 5 seconds and max retries to 2
}
// Function to add click listeners for magnet and download links
function addClickListeners(links, type) {
links.forEach((link) => {
link.addEventListener('click', function(){
let href = this.getAttribute('href');
if (href === 'javascript:void(0)') {
let tLink = this.getAttribute('data-href');
GM_xmlhttpRequest({
method: 'GET',
url: tLink,
onload: function (response) {
let container = document.implementation.createHTMLDocument().documentElement;
container.innerHTML = response.responseText;
let retrievedLink = (type === 'ml') ? container.querySelector('a[href^="magnet:"]') : container.querySelector('.dropdown-menu > li > a');
if (retrievedLink) {
link.setAttribute('href', retrievedLink.href.replace('http:', 'https:'));
link.click();
}
}
});
}
}, false);
});
}
function optimizeImageUrl(imgSrc) {
const optimizations = [
{ from: 'https://imgtraffic.com/1s/', to: 'https://imgtraffic.com/1/' },
{ from: /https?:\/\/.*\/images\/.*\.th\.jpg$/, to: (url) => url.replace(/\.th\.jpg$/, '.jpg') },
{ from: 'https://22pixx.xyz/as/', to: 'https://22pixx.xyz/a/' },
{ from: 'http://imgblaze.net/data_server_', to: 'https://www.imgopaleno.site/data_server_' },
{ from: '/small/small_', to: '/big/' }
];
return optimizations.reduce((url, opt) => {
if (typeof opt.from === 'string') {
return url.replace(opt.from, opt.to);
} else if (opt.from instanceof RegExp) {
return opt.from.test(url) ? url.replace(opt.from, opt.to) : url;
}
return url;
}, imgSrc);
}
function appendImages(link, doc) {
if (!config.showThumbnails) return;
// Check if thumbnails are already added
if (link.parentNode.querySelector('.thumbnail-container')) {
return;
}
let images = doc.querySelectorAll('#description img');
if (images.length > 0) {
let flexContainer = document.createElement('div');
flexContainer.classList.add('thumbnail-container');
flexContainer.style.display = 'flex';
flexContainer.style.flexWrap = 'wrap';
flexContainer.style.gap = '10px';
flexContainer.style.marginTop = '10px';
let clonedImages = [];
images.forEach((img, index) => {
let clonedImg = img.cloneNode(true);
let imgSrc = img.getAttribute('data-original') || img.src;
// URL restructuring for larger resolutions
if (imgSrc.includes('https://imgtraffic.com/1s/')) {
imgSrc = imgSrc.replace('https://imgtraffic.com/1s/', 'https://imgtraffic.com/1/');
}
if (imgSrc.includes('https://pilot007.org/images/')) {
imgSrc = imgSrc.replace(/.th\.jpg$/, '.jpg');
}
if (imgSrc.includes('https://13xpics.space/images/')) {
imgSrc = imgSrc.replace(/.th\.jpg$/, '.jpg');
}
if (imgSrc.includes('https://37xpics.space/images/')) {
imgSrc = imgSrc.replace(/.th\.jpg$/, '.jpg');
}
if (/https?:\/\/.*\/images\/.*\.th\.jpg$/.test(imgSrc)) {
imgSrc = imgSrc.replace(/\.th\.jpg$/, '.jpg');
}
if (imgSrc.includes('https://22pixx.xyz/as/')) {
imgSrc = imgSrc.replace('https://22pixx.xyz/as/', 'https://22pixx.xyz/a/');
}
if (imgSrc.includes('http://imgblaze.net/data_server_')) {
imgSrc = imgSrc.replace('http://imgblaze.net/data_server_', 'https://www.imgopaleno.site/data_server_')
.replace('/small/small_', '/big/');
}
imgSrc = optimizeImageUrl(imgSrc);
clonedImg.src = imgSrc;
clonedImg.style.maxHeight = '100px';
clonedImg.style.setProperty('margin', '0', 'important');
clonedImg.style.display = index < config.visibleImages ? 'block' : 'none';
flexContainer.appendChild(clonedImg);
clonedImages.push(clonedImg);
// Add mouseover events to each image
clonedImg.addEventListener('mouseover', function(e) {
showEnlargedImg(imgSrc, e);
});
clonedImg.addEventListener('mousemove', updateEnlargedImgPosition);
clonedImg.addEventListener('mouseout', removeEnlargedImg);
});
if (images.length > config.visibleImages) {
let showMoreButton = document.createElement('button');
showMoreButton.textContent = 'Show More';
showMoreButton.onclick = function () {
let isShowingMore = showMoreButton.textContent === 'Show Less';
clonedImages.forEach((img, index) => {
if (index >= config.visibleImages) {
img.style.display = isShowingMore ? 'none' : 'block';
}
});
showMoreButton.textContent = isShowingMore ? 'Show More' : 'Show Less';
};
flexContainer.appendChild(showMoreButton);
}
link.parentNode.insertBefore(flexContainer, link.nextSibling);
}
}
/* clonedImg.src = imgSrc;
clonedImg.style.maxHeight = '100px';
clonedImg.style.setProperty('margin', '0', 'important');
clonedImg.style.display = index < config.visibleImages ? 'block' : 'none';
flexContainer.appendChild(clonedImg);
clonedImages.push(clonedImg);
// Add mouseover events to each image
clonedImg.addEventListener('mouseover', function(e) {
showEnlargedImg(imgSrc, e);
});
clonedImg.addEventListener('mousemove', updateEnlargedImgPosition);
clonedImg.addEventListener('mouseout', removeEnlargedImg);
});
if (images.length > config.visibleImages) {
let showMoreButton = document.createElement('button');
showMoreButton.textContent = 'Show More';
showMoreButton.onclick = function () {
let isShowingMore = showMoreButton.textContent === 'Show Less';
clonedImages.forEach((img, index) => {
if (index >= config.visibleImages) {
img.style.display = isShowingMore ? 'none' : 'block';
}
});
showMoreButton.textContent = isShowingMore ? 'Show More' : 'Show Less';
};
flexContainer.appendChild(showMoreButton);
}
link.parentNode.insertBefore(flexContainer, link.nextSibling);
}
}*/
// Function to show an enlarged image
/*function showEnlargedImg(imgSrc, event) {
removeEnlargedImg(); // Remove any existing enlarged image
const enlargedImg = document.createElement('img');
enlargedImg.src = imgSrc;
enlargedImg.id = 'x1337-enlarged-img';
enlargedImg.style.position = 'fixed';
enlargedImg.style.maxWidth = '500px';
enlargedImg.style.maxHeight = '500px';
enlargedImg.style.zIndex = '10000';
enlargedImg.style.border = '2px solid #F14E13';
enlargedImg.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
document.body.appendChild(enlargedImg);
updateEnlargedImgPosition(event);
}*/
// Function to show an enlarged image
function showEnlargedImg(imgSrc, event) {
removeEnlargedImg(); // Remove any existing enlarged image
const enlargedImg = document.createElement('img');
enlargedImg.src = imgSrc;
enlargedImg.id = 'x1337-enlarged-img';
enlargedImg.style.position = 'fixed';
enlargedImg.style.zIndex = '10000';
enlargedImg.style.border = '2px solid #F14E13';
enlargedImg.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
// Set max dimensions while maintaining aspect ratio
enlargedImg.style.maxWidth = '500px'; // Set max width
enlargedImg.style.maxHeight = '500px'; // Set max height
enlargedImg.style.width = 'auto'; // Set width to auto to maintain aspect ratio
enlargedImg.style.height = 'auto'; // Set height to auto to maintain aspect ratio
// Append the enlarged image to the document
document.body.appendChild(enlargedImg);
updateEnlargedImgPosition(event);
}
// Function to update the position of the enlarged image
function updateEnlargedImgPosition(e) {
const enlargedImg = document.getElementById('x1337-enlarged-img');
if (enlargedImg) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const imgWidth = enlargedImg.offsetWidth;
const imgHeight = enlargedImg.offsetHeight;
let left = e.clientX + 20; // 20px offset from cursor
let top = e.clientY + 20;
// Adjust position if it goes off-screen
if (left + imgWidth > viewportWidth) {
left = e.clientX - imgWidth - 20;
}
if (top + imgHeight > viewportHeight) {
top = e.clientY - imgHeight - 20;
}
enlargedImg.style.left = `${left}px`;
enlargedImg.style.top = `${top}px`;
}
}
// Function to remove enlarged image
function removeEnlargedImg() {
const enlargedImg = document.getElementById('x1337-enlarged-img');
if (enlargedImg) {
document.body.removeChild(enlargedImg);
}
}
// Function to optimize image URL
function optimizeImageUrl(imgSrc) {
const optimizations = [
{ from: 'https://imgtraffic.com/1s/', to: 'https://imgtraffic.com/1/' },
{ from: /https?:\/\/.*\/images\/.*\.th\.jpg$/, to: (url) => url.replace(/\.th\.jpg$/, '.jpg') },
{ from: 'https://22pixx.xyz/as/', to: 'https://22pixx.xyz/a/' },
{ from: 'http://imgblaze.net/data_server_', to: 'https://www.imgopaleno.site/data_server_' },
{ from: '/small/small_', to: '/big/' }
];
return optimizations.reduce((url, opt) => {
if (typeof opt.from === 'string') {
return url.replace(opt.from, opt.to);
} else if (opt.from instanceof RegExp) {
return opt.from.test(url) ? url.replace(opt.from, opt.to) : url;
}
return url;
}, imgSrc);
}
// Function to inject custom CSS
function injectCustomCSS() {
let customCSS = `
.list-button-magnet > i.flaticon-magnet {
font-size: 13px;
color: #da3a04
}
.list-button-dl > i.flaticon-torrent-download {
font-size: 13px;
color: #89ad19;
}
table.table-list td.dl-buttons {
border-left: 1px solid #f6f6f6;
border-right: 1px solid #c0c0c0;
padding-left: 2.5px;
padding-right: 2.5px;
text-align: center !important;
position: relative;
display: table-cell !important;
width: 6%;
}
td.dl-buttons > a,
td.dl-buttons > a:hover,
td.dl-buttons > a:visited,
td.dl-buttons > a:link,
td.dl-buttons > a:active {
color: inherit;
text-decoration: none;
cursor: pointer;
display: inline-block !important;
margin: 0 2px;
}
table.table-list td.coll-1b {
border-right: 1px solid silver;
}
.table-list > thead > tr > th:nth-child(2),
.table-list > thead > tr > td:nth-child(2) {
text-align: center;
}
`;
if (config.fullWidthSite) {
customCSS += `
.container { max-width: none !important; }
main.container, div.container { max-width: 1450px; }
`;
}
GM_addStyle(customCSS);
}
// Function to clean the page title
function cleanTitle(title) {
if (title.startsWith('Download ')) {
title = title.substring('Download '.length);
}
let pipeIndex = title.indexOf(' Torrent |');
if (pipeIndex !== -1) {
title = title.substring(0, pipeIndex);
}
return title;
}
// Function to modify H1 content on torrent pages
function modifyH1ContentOnTorrentPages() {
if (window.location.pathname.startsWith('/torrent/')) {
let h1Element = document.querySelector('.box-info-heading h1');
if (h1Element) {
h1Element.textContent = cleanTitle(document.title);
}
}
}
// Queue for managing fetch requests
const fetchQueue = [];
let isProcessingQueue = false;
// Function to add a fetch request to the queue
/*function queueFetchRequest(link, onSuccess, retryDelay = 5000, maxRetries = 2) {
fetchQueue.push({ link, onSuccess, retryDelay, maxRetries, retries: 0 });
if (!isProcessingQueue) {
processQueue();
}
}*/
function queueFetchRequest(link, onSuccess) {
fetchQueue.push({ link, onSuccess, retries: 0 });
if (!isProcessingQueue) {
processQueue();
}
}
// Function to process the queue
/*function processQueue() {
if (fetchQueue.length === 0) {
isProcessingQueue = false;
return;
}
isProcessingQueue = true;
const { link, onSuccess, retryDelay, maxRetries, retries } = fetchQueue.shift();
fetchContent(link, onSuccess, retryDelay, maxRetries, retries);
}*/
function processQueue() {
if (fetchQueue.length === 0) {
isProcessingQueue = false;
return;
}
isProcessingQueue = true;
const { link, onSuccess, retries } = fetchQueue.shift();
fetchContent(link, onSuccess, retries);
}
// Modified fetchContent function with adjustable delay
/*function fetchContent(link, onSuccess, retries = 0) {
console.log(`Attempting to fetch ${link} (Attempt ${retries + 1} of ${config.maxRetries + 1})`);
GM_xmlhttpRequest({
method: 'GET',
url: link,
onload: function(response) {
console.log(`Received response for ${link} with status ${response.status}`);
if (response.status === 200) {
console.log(`Successfully fetched ${link}`);
let parser = new DOMParser();
let doc = parser.parseFromString(response.responseText, 'text/html');
console.log(`Parsed HTML content for ${link}`);
onSuccess(doc);
setTimeout(processQueue, config.queueFetchDelay);
} else if (response.status === 429 && retries < config.maxRetries) {
console.warn(`Rate limited for ${link}. Retrying in ${config.queueFetchDelay * 10}ms (Retry ${retries + 1} of ${config.maxRetries})`);
fetchQueue.push({ link, onSuccess, retries: retries + 1 });
setTimeout(processQueue, config.queueFetchDelay * 10);
} else {
console.error(`Failed to fetch ${link} after ${retries} retries. Status: ${response.status}`);
console.error(`Response text: ${response.responseText}`);
showPopup(`Failed to fetch ${link} after ${retries} retries`);
setTimeout(processQueue, config.queueFetchDelay);
}
},
onerror: function(error) {
console.error(`Error fetching ${link}:`, error);
console.error(`Error details:`, JSON.stringify(error));
setTimeout(processQueue, config.queueFetchDelay);
},
ontimeout: function() {
console.error(`Request timed out for ${link}`);
setTimeout(processQueue, config.queueFetchDelay);
}
});
}*/
function fetchContent(link, onSuccess, retries = 0) {
console.log(`Attempting to fetch ${link} (Attempt ${retries + 1} of ${config.maxRetries + 1})`);
GM_xmlhttpRequest({
method: 'GET',
url: link,
onload: function(response) {
console.log(`Received response for ${link} with status ${response.status}`);
if (response.status === 200) {
console.log(`Successfully fetched ${link}`);
let parser = new DOMParser();
let doc = parser.parseFromString(response.responseText, 'text/html');
console.log(`Parsed HTML content for ${link}`);
onSuccess(doc);
setTimeout(processQueue, config.queueFetchDelay);
} else if (response.status === 429 && retries < config.maxRetries) {
console.warn(`Rate limited for ${link}. Retrying in ${config.queueFetchDelay * 10}ms (Retry ${retries + 1} of ${config.maxRetries})`);
fetchQueue.push({ link, onSuccess, retries: retries + 1 });
setTimeout(processQueue, config.queueFetchDelay * 10);
} else {
console.error(`Failed to fetch ${link} after ${retries} retries. Status: ${response.status}`);
console.error(`Response text: ${response.responseText}`);
showPopup(`Failed to fetch ${link} after ${retries} retries`);
setTimeout(processQueue, config.queueFetchDelay);
}
},
onerror: function(error) {
console.error(`Error fetching ${link}:`, error);
console.error(`Error details:`, JSON.stringify(error));
setTimeout(processQueue, config.queueFetchDelay);
},
ontimeout: function() {
console.error(`Request timed out for ${link}`);
setTimeout(processQueue, config.queueFetchDelay);
}
});
}
// Function to process a link
function processLink(link) {
// Find the parent row of the link
const row = link.closest('tr');
// Check if the row has already been processed
if (row.dataset.processed === 'true') {
return;
}
queueFetchRequest(link.href, (doc) => {
let torrentLink = doc.querySelector("a[href*='itorrents.org/torrent/']");
let magnetLink = doc.querySelector("a[href^='magnet:?']");
updateLinkTitle(link, doc);
if (config.showThumbnails) {
appendImages(link, doc);
}
addDownloadButtons(link, torrentLink, magnetLink);
// Mark the row as processed
row.dataset.processed = 'true';
});
}
// Function to update the link title
function updateLinkTitle(link, doc) {
let title = cleanTitle(doc.querySelector('title').innerText);
link.innerText = title;
}
// Function to add download buttons next to the link
function addDownloadButtons(link, torrentLink, magnetLink) {
// Check if buttons are already added
let existingTorrentButton = link.parentNode.querySelector('#DLT');
let existingMagnetButton = link.parentNode.querySelector('#DLM');
// Create a container for buttons if not already present
let buttonsContainer = link.parentNode.querySelector('.buttons-container');
if (!buttonsContainer) {
buttonsContainer = document.createElement('div');
buttonsContainer.classList.add('buttons-container');
buttonsContainer.style.display = 'flex';
buttonsContainer.style.alignItems = 'center';
buttonsContainer.style.gap = '5px';
buttonsContainer.style.marginTop = '10px';
link.after(buttonsContainer);
}
// Add torrent button if it doesn't already exist
if (torrentLink && !existingTorrentButton) {
let torrentButton = document.createElement('a');
torrentButton.href = torrentLink.href.replace('http:', 'https:');
torrentButton.title = 'Download torrent file';
torrentButton.id = 'DLT'; // Set the ID
torrentButton.innerHTML = '<i class="flaticon-torrent-download" style="color: #89ad19; font-size: 16px"></i>';
buttonsContainer.appendChild(torrentButton);
}
// Add magnet button if it doesn't already exist
if (magnetLink && !existingMagnetButton) {
let magnetButton = document.createElement('a');
magnetButton.href = magnetLink.href;
magnetButton.title = 'Download via magnet';
magnetButton.id = 'DLM'; // Set the ID
magnetButton.innerHTML = '<i class="flaticon-magnet" style="color: #da3a04; font-size: 16px"></i>';
buttonsContainer.appendChild(magnetButton);
}
}
// Function to update download buttons
function updateDownloadButtons() {
document.querySelectorAll('.table-list-wrap .table-list tbody tr').forEach(row => {
let torrentPageLink = row.querySelector('.coll-1.name a').getAttribute('href');
let fullTorrentPageLink = `https://1337x.to${torrentPageLink}`;
//fetchContent(fullTorrentPageLink, (doc) => {
fetchContent(fullTorrentPageLink, (doc) => {
let torrentLink = doc.querySelector("a[href*='itorrents.org/torrent/']");
let magnetLink = doc.querySelector("a[href^='magnet:?']");
let dlButtonsCell = row.querySelector('.coll-1b.dl-buttons');
if (dlButtonsCell) {
updateOrCreateButtons(dlButtonsCell, torrentLink, magnetLink);
}
});
});
}
// Function to update or create buttons
function updateOrCreateButtons(dlButtonsCell, torrentLink, magnetLink) {
let existingButtons = dlButtonsCell.querySelectorAll('a');
if (existingButtons.length === 2) {
if (torrentLink) existingButtons[0].href = torrentLink.href.replace('http:', 'https:');
if (magnetLink) existingButtons[1].href = magnetLink.href;
} else {
dlButtonsCell.innerHTML = createButtonHTML(torrentLink, magnetLink);
}
}
// Function to create HTML for download and magnet buttons
function createButtonHTML(torrentLink, magnetLink) {
let torrentButtonHTML = torrentLink ? `<a href="${torrentLink.href.replace('http:', 'https:')}" title="Download torrent file"><i class="flaticon-torrent-download" style="color: #89ad19; font-size: 16px"></i></a>` : '';
let magnetButtonHTML = magnetLink ? `<a href="${magnetLink.href}" title="Download via magnet"><i class="flaticon-magnet" style="color: #da3a04; font-size: 16px"></i></a>` : '';
return `<div style="display: flex; align-items: center; gap: 5px; margin-top: 10px;">${torrentButtonHTML}${magnetButtonHTML}</div>`;
}
// Function to replace link text with titles and append images
function replaceLinkTextWithTitlesAndAppendImages() {
let unprocessedRows = document.querySelectorAll('.table-list tbody tr:not([data-processed])');
unprocessedRows.forEach(row => {
let link = row.querySelector('a[href^="/torrent/"]');
if (link) {
processLink(link);
}
});
}
/*function showPopup(message, duration = 10000) {
const popup = document.createElement('div');
popup.id = 'x1337-popup';
popup.textContent = message;
document.body.appendChild(popup);
// Fade in
setTimeout(() => {
popup.style.opacity = '1';
}, 10);
// Fade out and remove
setTimeout(() => {
popup.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(popup);
}, 300);
}, duration);
}*/
let popupQueue = [];
let isShowingPopup = false;
function showPopup(message, duration = 10000) {
popupQueue.push({ message, duration });
if (!isShowingPopup) {
displayNextPopup();
}
}
function displayNextPopup() {
if (popupQueue.length === 0) {
isShowingPopup = false;
return;
}
isShowingPopup = true;
const { message, duration } = popupQueue.shift();
const popup = document.createElement('div');
popup.id = 'x1337-popup';
popup.textContent = message;
document.body.appendChild(popup);
// Fade in
setTimeout(() => {
popup.style.opacity = '1';
}, 10);
// Fade out and remove
setTimeout(() => {
popup.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(popup);
displayNextPopup(); // Show next popup in queue
}, 300);
}, duration);
}
// Save settings
function addSettingsMenu() {
GM_addStyle(settingsCSS);
document.body.insertAdjacentHTML('beforeend', settingsHTML);
// Toggle settings menu
document.getElementById('x1337-settings-toggle').addEventListener('click', function() {
const wrapper = document.getElementById('x1337-settings-wrapper');
wrapper.style.right = wrapper.style.right === '0px' ? '-300px' : '0px';
});
// Add event listener for showing/hiding the visible images input
document.getElementById('x1337-show-thumbnails').addEventListener('change', function() {
const visibleImagesOption = document.querySelector('.x1337-sub-option');
visibleImagesOption.style.display = this.checked ? 'block' : 'none';
})
// Save settings
document.getElementById('x1337-save-settings').addEventListener('click', function() {
config.showThumbnails = document.getElementById('x1337-show-thumbnails').checked;
config.showExtraColumn = document.getElementById('x1337-show-magnet-column').checked;
config.fullWidthSite = document.getElementById('x1337-full-width-site').checked;
config.visibleImages = parseInt(document.getElementById('x1337-visible-images').value);
config.queueFetchDelay = parseInt(document.getElementById('x1337-queue-fetch-delay').value);
config.maxRetries = parseInt(document.getElementById('x1337-max-retries').value);
GM_setValue('showThumbnails', config.showThumbnails);
GM_setValue('showExtraColumn', config.showExtraColumn);
GM_setValue('fullWidthSite', config.fullWidthSite);
GM_setValue('visibleImages', config.visibleImages);
GM_setValue('queueFetchDelay', config.queueFetchDelay);
GM_setValue('maxRetries', config.maxRetries);
// Apply changes immediately
applySettings();
// Show the popup
showPopup('Settings saved successfully!');
//console.error('Settings saved successfully!');
});
}
function applySettings() {
if (config.showExtraColumn) {
appendColumn();
} else {
// Remove extra column if it exists, including the header
document.querySelectorAll('.table-list-wrap').forEach(table => {
const header = table.querySelector('.table-list > thead > tr > th.coll-1b');
if (header) header.remove();
table.querySelectorAll('.table-list > tbody > tr > td.coll-1b.dl-buttons').forEach(cell => {
cell.remove();
});
});
extraColumnAdded = false; // Reset the flag
}
if (config.fullWidthSite) {
document.body.classList.add('full-width-site');
} else {
document.body.classList.remove('full-width-site');
}
// Re-apply thumbnails settings and update titles and download buttons
replaceLinkTextWithTitlesAndAppendImages();
// Update CSS for full width if needed
injectCustomCSS();
}
// Add this near the end of your script, just before the init() call
function addMutationObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && node.matches('.table-list tbody tr')) {
let link = node.querySelector('a[href^="/torrent/"]');
if (link) {
processLink(link);
}
}
});
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
let hasInitialized = false;
function init() {
if (hasInitialized) return;
hasInitialized = true;
showPopup("1337x Enhancements v11 (by sharmanhall)", 5000);
addSettingsMenu();
injectCustomCSS();
applySettings();
modifyH1ContentOnTorrentPages();
replaceLinkTextWithTitlesAndAppendImages();
// Add mutation observer to handle dynamically loaded content
addMutationObserver();
}
// Run the script
init();
})();