// ==UserScript==
// @name Real-Debrid Enhancer
// @namespace http://tampermonkey.net/
// @version 2.1
// @description Enhance Real-Debrid with clickable rows, copy and debrid buttons, grid layout, and improved layout management on torrents and downloader pages.
// @author UnderPL
// @license MIT
// @match https://real-debrid.com/torrents*
// @match https://real-debrid.com/
// @match https://real-debrid.com/downloader*
// @grant GM_setClipboard
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
let copyButton, debridButton;
GM_addStyle(`
.tr.g1:not(.warning), .tr.g2:not(.warning), .tr.g1:not(.warning) + tr, .tr.g2:not(.warning) + tr {
cursor: pointer;
position: relative;
}
.tr.g1.selected, .tr.g2.selected, .tr.g1.selected + tr, .tr.g2.selected + tr {
background-color: rgba(0, 255, 0, 0.3);
}
.tr.g1, .tr.g2 {
border-top: 2px solid black/* Green border on top */
}
.tr.g1 + tr, .tr.g2 + tr {
border-bottom: 2px solid black; /* Green border on bottom */
}
#buttonContainer {
position: fixed;
bottom: 10px;
right: 10px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 9999;
}
#buttonContainer button {
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
#facebox .content {
width: 90vw !important;
max-width: 1200px !important;
display: flex !important;
flex-wrap: wrap !important;
justify-content: space-between !important;
}
.torrent-info {
width: calc(33.33% - 20px);
margin-bottom: 20px;
border: 1px solid #ccc;
padding: 10px;
box-sizing: border-box;
}
`);
function initializeApplication() {
if (window.location.href.includes('/torrents')) {
cleanupTorrentPageLayout();
createFloatingButtons();
makeItemsSelectable();
updateFloatingButtonsVisibility();
setupTorrentInfoWindowObserver();
checkForTorrentInfoWindow();
setupItemHoverEffects();
movePaginationToBottomRight();
addSwitchToGridLayoutButton(); //comment this and uncomment line below to automatically switch to the more compact version of the torrent page
//switchToGridLayout()
}
if (window.location.href === 'https://real-debrid.com/' || window.location.href.includes('/downloader')) {
addExtractUrlsButtonToDownloader();
addCopyLinksButton();
}
}
function movePaginationToBottomRight() {
const parentElement = document.querySelector('div.full_width_wrapper');
const formElement = parentElement.querySelector('form:nth-child(1)');
const pageElements = parentElement.querySelectorAll('div.full_width_wrapper > strong, div.full_width_wrapper > a[href^="./torrents?p="]');
const containerDiv = document.createElement('div');
const marginSize = '5px';
const fontSize = '16px';
containerDiv.style.position = 'absolute';
containerDiv.style.right = '0';
containerDiv.style.bottom = '0';
containerDiv.style.display = 'flex';
containerDiv.style.gap = marginSize;
containerDiv.style.fontSize = fontSize;
pageElements.forEach(page => {
containerDiv.appendChild(page);
});
formElement.style.position = 'relative';
formElement.appendChild(containerDiv);
}
function createFloatingButtons() {
const container = document.createElement('div');
container.id = 'buttonContainer';
debridButton = document.createElement('button');
debridButton.addEventListener('click', sendSelectedLinksToDebrid);
copyButton = document.createElement('button');
copyButton.addEventListener('click', copySelectedLinksToClipboard);
container.appendChild(debridButton);
container.appendChild(copyButton);
document.body.appendChild(container);
return container;
}
function updateFloatingButtonsVisibility() {
const selectedLinks = getSelectedItemLinks();
const count = selectedLinks.length;
if (count > 0) {
debridButton.textContent = `Debrid (${count})`;
copyButton.textContent = `Copy Selected to Clipboard (${count})`;
debridButton.style.display = 'block';
copyButton.style.display = 'block';
} else {
debridButton.style.display = 'none';
copyButton.style.display = 'none';
}
}
function makeItemsSelectable() {
const rows = document.querySelectorAll('.tr.g1, .tr.g2');
rows.forEach(row => {
const warningSpan = row.querySelector('span.px10 strong');
if (!warningSpan || warningSpan.textContent !== 'Warning:') {
const nextRow = row.nextElementSibling;
const clickHandler = () => {
row.classList.toggle('selected');
if (nextRow) {
nextRow.classList.toggle('selected');
}
if (row.classList.contains('selected')) {
row.style.backgroundColor = 'rgba(0, 255, 0, 0.3)';
if (nextRow) nextRow.style.backgroundColor = 'rgba(0, 255, 0, 0.3)';
} else {
row.style.backgroundColor = '';
if (nextRow) nextRow.style.backgroundColor = '';
}
updateFloatingButtonsVisibility();
};
row.addEventListener('click', clickHandler);
if (nextRow) {
nextRow.addEventListener('click', clickHandler);
}
} else {
row.classList.add('warning');
if (row.nextElementSibling) {
row.nextElementSibling.classList.add('warning');
}
}
});
const entries = document.querySelectorAll('.torrent-entry');
entries.forEach(entry => {
entry.addEventListener('click', () => {
entry.classList.toggle('selected');
if (entry.classList.contains('selected')) {
entry.style.backgroundColor = 'rgba(0, 255, 0, 0.3)';
} else {
entry.style.backgroundColor = '';
}
updateFloatingButtonsVisibility();
});
});
}
function setupItemHoverEffects() {
const rows = document.querySelectorAll('.tr.g1, .tr.g2');
rows.forEach(row => {
const nextRow = row.nextElementSibling;
if (nextRow && !nextRow.classList.contains('g1') && !nextRow.classList.contains('g2')) {
row.addEventListener('mouseenter', () => {
if (!row.classList.contains('selected')) {
row.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
nextRow.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
}
});
row.addEventListener('mouseleave', () => {
if (!row.classList.contains('selected')) {
row.style.backgroundColor = '';
nextRow.style.backgroundColor = '';
}
});
nextRow.addEventListener('mouseenter', () => {
if (!row.classList.contains('selected')) {
row.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
nextRow.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
}
});
nextRow.addEventListener('mouseleave', () => {
if (!row.classList.contains('selected')) {
row.style.backgroundColor = '';
nextRow.style.backgroundColor = '';
}
});
}
});
const entries = document.querySelectorAll('.torrent-entry');
entries.forEach(entry => {
entry.addEventListener('mouseenter', () => {
if (!entry.classList.contains('selected')) {
entry.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
}
});
entry.addEventListener('mouseleave', () => {
if (!entry.classList.contains('selected')) {
entry.style.backgroundColor = '';
}
});
});
}
function getSelectedItemLinks() {
const selectedLinks = [];
const selectedRows = document.querySelectorAll('.tr.g1.selected, .tr.g2.selected');
const selectedEntries = document.querySelectorAll('.torrent-entry.selected');
selectedRows.forEach(row => {
const textarea = row.nextElementSibling.querySelector('textarea');
if (textarea) {
selectedLinks.push(textarea.value);
}
});
selectedEntries.forEach(entry => {
const textarea = entry.querySelector('textarea');
if (textarea) {
selectedLinks.push(textarea.value);
}
});
return selectedLinks;
}
function copySelectedLinksToClipboard() {
const selectedLinks = getSelectedItemLinks();
if (selectedLinks.length > 0) {
const clipboardText = selectedLinks.join('\n');
GM_setClipboard(clipboardText);
}
}
function sendSelectedLinksToDebrid(e) {
e.preventDefault();
const selectedLinks = getSelectedItemLinks();
if (selectedLinks.length > 0) {
const form = document.createElement('form');
form.method = 'POST';
form.action = './downloader';
const input = document.createElement('textarea');
input.name = 'links';
input.value = selectedLinks.join('\n');
form.appendChild(input);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
}
function extractUrlsFromText(text) {
const urlRegex = /(?:(?:https?):\/\/|www\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])/igm;
return text.match(urlRegex) || [];
}
function addExtractUrlsButtonToDownloader() {
const textarea = document.getElementById('links');
if (textarea) {
const button = document.createElement('button');
button.id = 'extractUrlsButton';
button.textContent = 'Extract URL(s)';
button.style.position = 'absolute';
button.style.right = '28px';
button.style.top = '0';
button.addEventListener('click', function(e) {
e.preventDefault();
const content = textarea.value;
const urls = extractUrlsFromText(content);
textarea.value = urls.join('\n');
});
textarea.parentNode.style.position = 'relative';
textarea.parentNode.appendChild(button);
}
}
function addCopyLinksButton() {
const linksContainer = document.querySelector('#links-container');
if (linksContainer && linksContainer.children.length > 0) {
const originalButton = document.querySelector('#sub_links');
if (originalButton) {
const copyButton = originalButton.cloneNode(true);
copyButton.id = 'copy_links';
copyButton.value = 'Copy links';
copyButton.type = 'button';
copyButton.style.display = 'block';
copyButton.style.margin = '0 auto';
copyButton.style.float = 'none'
copyButton.style.marginBottom = '10px'
copyButton.addEventListener('click', function(e) {
e.preventDefault();
const links = Array.from(document.querySelectorAll('#links-container .link-generated a'))
.filter(a => a.textContent.includes('DOWNLOAD'))
.map(a => a.href)
.join('\n');
if (links) {
GM_setClipboard(links);
copyButton.value = 'Copy Links ✔️';
setTimeout(() => {
copyButton.value = 'Copy links';
}, 1500);
}
});
linksContainer.insertAdjacentElement('afterend', copyButton);
}
}
}
function cleanupTorrentPageLayout() {
const textContainer = document.querySelector('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper');
if (textContainer) {
Array.from(textContainer.childNodes).forEach(node => {
if (node.nodeType === Node.TEXT_NODE) {
node.remove();
}
});
}
const brElements = document.querySelectorAll('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper br');
brElements.forEach(br => br.remove());
const centerElements = document.querySelectorAll('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper center');
centerElements.forEach(center => center.remove());
const contentSeparatorMiniElements = document.querySelectorAll('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper div.content_separator_mini');
contentSeparatorMiniElements.forEach(div => div.remove());
const h2Elements = document.querySelectorAll('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper h2');
h2Elements.forEach(h2 => h2.remove());
const spanElements = document.querySelectorAll('html.cufon-active.cufon-ready body div#block div#contentblock div#wrapper_global div.main_content_wrapper div.full_width_wrapper span.px10');
spanElements.forEach(span => span.remove());
}
function redesignTorrentInfoWindow() {
const facebox = document.getElementById('facebox');
if (facebox) {
const content = facebox.querySelector('.content');
if (content) {
content.style.width = '90vw';
content.style.maxWidth = '1200px';
content.style.display = 'flex';
content.style.flexWrap = 'wrap';
content.style.justifyContent = 'space-between';
// Store the original buttons with their event listeners
const startButtons = Array.from(content.querySelectorAll('input[type="button"][value="Start my torrent"]'));
const torrentInfos = content.innerHTML.split('<h2>Torrent Files</h2>').filter(info => info.trim() !== '');
content.innerHTML = '';
torrentInfos.forEach((info, index) => {
const div = document.createElement('div');
div.className = 'torrent-info';
// Create a temporary div to parse the HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = '<h2>Torrent Files</h2>' + info;
// Move the content except the button
while (tempDiv.firstChild) {
if (tempDiv.firstChild.tagName !== 'INPUT' || tempDiv.firstChild.type !== 'button') {
div.appendChild(tempDiv.firstChild);
} else {
tempDiv.removeChild(tempDiv.firstChild);
}
}
// Append the original button with its event listeners
if (startButtons[index]) {
div.appendChild(startButtons[index]);
}
content.appendChild(div);
});
}
}
}
function setupTorrentInfoWindowObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
for (let node of mutation.addedNodes) {
if (node.id === 'facebox') {
redesignTorrentInfoWindow();
}
}
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
function checkForTorrentInfoWindow() {
const intervalId = setInterval(() => {
const facebox = document.getElementById('facebox');
if (facebox) {
redesignTorrentInfoWindow();
clearInterval(intervalId);
}
}, 1000);
}
function createGridLayout(columnCount) {
const table = document.querySelector('table[width="100%"]');
if (!table) return;
const container = document.createElement('div');
container.id = 'torrent-grid-container';
container.style.display = 'flex';
container.style.flexWrap = 'wrap';
container.style.justifyContent = 'space-between';
const rows = table.querySelectorAll('tr');
for (let i = 1; i < rows.length; i += 2) {
const torrentDiv = createGridItemFromTableRows(rows[i], rows[i + 1]);
container.appendChild(torrentDiv);
}
table.parentNode.replaceChild(container, table);
applyGridLayoutStyles(columnCount);
adjustImageSizeInNewLayout();
moveDeleteLinkToEnd();
makeItemsSelectable();
setupItemHoverEffects();
}
function applyGridLayoutStyles(columnCount) {
const width = `calc(${100 / columnCount}% - 20px)`;
GM_addStyle(`
#torrent-grid-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.torrent-entry {
width: ${width};
margin-bottom: 20px;
border: 1px solid #ccc;
padding: 10px;
box-sizing: border-box;
cursor: pointer;
}
.torrent-entry.selected {
background-color: rgba(0, 255, 0, 0.3) !important;
}
.torrent-entry:hover:not(.selected) {
background-color: rgba(0, 255, 0, 0.1);
}
.torrent-entry td {
display: block;
width: 100%;
}
.torrent-entry tr {
display: block;
}
.torrent-entry form {
margin-top: 10px;
}
.torrent-entry textarea {
min-height: 2.5em;
max-height: 6em;
overflow-y: auto;
resize: vertical;
}
`);
}
function adjustImageSizeInNewLayout() {
document.querySelectorAll('#torrent-grid-container .torrent-entry form input[type="image"]').forEach(function(img) {
img.style.width = '10%';
img.style.height = 'auto';
img.style.display = 'inline-block';
img.style.marginLeft = '10px';
});
document.querySelectorAll('#torrent-grid-container .torrent-entry form').forEach(function(form) {
form.style.display = 'flex';
form.style.alignItems = 'center';
});
}
function moveDeleteLinkToEnd() {
document.querySelectorAll('.torrent-entry').forEach(entry => {
const deleteLink = entry.querySelector('a[href*="del"]');
if (deleteLink) {
// Create a container for the delete link
const deleteContainer = document.createElement('div');
deleteContainer.classList.add('delete-container');
deleteContainer.style.position = 'absolute';
deleteContainer.style.right = '0';
deleteContainer.style.top = '0';
deleteContainer.style.display = 'flex';
deleteContainer.style.alignItems = 'center';
deleteContainer.style.height = '100%';
deleteContainer.style.paddingRight = '10px';
// Move the delete link into the new container
deleteContainer.appendChild(deleteLink);
entry.appendChild(deleteContainer);
// Ensure the parent .torrent-entry has relative positioning
entry.style.position = 'relative';
}
});
}
function createGridItemFromTableRows(mainRow, detailRow) {
const div = document.createElement('div');
div.className = 'torrent-entry';
div.innerHTML = mainRow.innerHTML + detailRow.innerHTML;
div.addEventListener('click', () => {
div.classList.toggle('selected');
updateFloatingButtonsVisibility();
});
return div;
}
function addSwitchToGridLayoutButton() {
const button = document.createElement('button');
button.textContent = 'Switch Layout';
button.id = 'switchLayoutButton';
button.style.position = 'fixed';
button.style.top = '10px';
button.style.right = '20px';
button.style.zIndex = '1000';
button.addEventListener('click', switchToGridLayout);
document.body.appendChild(button);
}
function switchToGridLayout() {
const columnCount = 3; // You can adjust this number as needed
createGridLayout(columnCount);
setupItemHoverEffects();
makeItemsSelectable();
updateFloatingButtonsVisibility();
const button = document.getElementById('switchLayoutButton');
if (button) {
button.remove();
}
}
if (document.readyState === 'complete') {
initializeApplication();
} else {
window.addEventListener('load', initializeApplication);
}
})();