- // ==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);
- }
- })();