您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Convert standard links into premium links using Real-Debrid
// ==UserScript== // @name Real-Debrid Premium Link Converter // @version 4 // @grant GM.xmlHttpRequest // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @namespace https://greasyfork.org/en/users/807108-jeremy-r // @include *://* // @exclude https://real-debrid.com/* // @description Convert standard links into premium links using Real-Debrid // @icon https://icons.duckduckgo.com/ip2/real-debrid.com.ico // @author JRem // @license MIT // ==/UserScript== let targetURLs = GM_getValue('targetURLs', []); let token = GM_getValue('api_token', []); // Register menu commands for the script function createMenu() { GM_registerMenuCommand("Update API Token", updatetoken); GM_registerMenuCommand("Refresh DDL Domains", updateDDLDomains); console.log("Menu commands registered."); } // Ensure `updateDDLDomains` runs at least once every 24 hours function ensureUpdateDDLDomains() { const lastRunTimestamp = GM_getValue('lastUpdateTimestamp', 0); const now = Date.now(); if (now - lastRunTimestamp >= 24 * 60 * 60 * 1000) { console.log("More than 24 hours since last update. Running `updateDDLDomains`..."); updateDDLDomains(); GM_setValue('lastUpdateTimestamp', now); } else { console.log("`updateDDLDomains` already ran within the last 24 hours."); } } // Fetch the domains from Real-Debrid API and update the domain cache function updateDDLDomains() { console.log("Updating DDL Domains..."); GM.xmlHttpRequest({ method: 'GET', url: 'https://api.real-debrid.com/rest/1.0/hosts/domains', headers: { 'Authorization': `Bearer ${GM_getValue('api_token', '')}` }, onload: function (response) { if (response.status === 200) { try { const domains = JSON.parse(response.responseText); if (domains.length > 0) { targetURLs = domains; // Replace the array with the new list GM_setValue('targetURLs', targetURLs); GM_setValue('lastUpdateTimestamp', Date.now()); console.log('Fetched domains:', targetURLs); showToast(`${domains.length} domains found and saved.`); } else { console.error('No domains found. Re-fetching...'); showToast('No domains found. Attempting re-fetch...'); updateDDLDomains(); } } catch (error) { console.error('Failed to parse the API response:', error); } } else { console.error('Failed to fetch domains. Status:', response.status); } }, onerror: function () { console.error('An error occurred while fetching DDL domains.'); } }); } // Update the API token function updatetoken() { GM.xmlHttpRequest({ method: 'GET', url: 'https://real-debrid.com/apitoken', onload: function (response) { if (response.status === 200) { const responseText = response.responseText; const tokenMatch = responseText.match(/document\.querySelectorAll\('input\[name=private_token\]'\)\[0\]\.value\s*=\s*'([^']+)'/); if (tokenMatch && tokenMatch[1]) { const token = tokenMatch[1]; GM_setValue('api_token', token); // Save the token console.log('API token updated successfully:', token); // Debug output showToast('API token updated successfully.'); } else { console.error('Failed to extract the token. Prompting user...'); const manualToken = prompt('API token not found in the response. Please enter it manually:'); if (manualToken) { GM_setValue('api_token', manualToken); console.log('API token manually entered:', manualToken); showToast('API token saved successfully.'); } else { console.error('No token entered. Unable to proceed.'); showToast('Token update failed. Please try again.'); } } } else { console.error('Failed to fetch the API token. Status:', response.status); } }, onerror: function () { console.error('An error occurred while updating the API token.'); } }); } // Display a toast message in the center of the screen function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; toast.style.position = 'fixed'; toast.style.top = '50%'; toast.style.left = '50%'; toast.style.transform = 'translate(-50%, -50%)'; toast.style.backgroundColor = '#333'; toast.style.color = '#fff'; toast.style.padding = '10px 20px'; toast.style.borderRadius = '5px'; toast.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.5)'; toast.style.zIndex = '9999'; toast.style.fontSize = '16px'; toast.style.textAlign = 'center'; toast.style.animation = 'fadeOut 3s ease forwards'; document.body.appendChild(toast); // Remove the toast after the animation setTimeout(() => { document.body.removeChild(toast); }, 3000); // Add fade-out animation const style = document.createElement('style'); style.textContent = ` @keyframes fadeOut { 0% { opacity: 1; } 80% { opacity: 0.8; } 100% { opacity: 0; } } `; document.head.appendChild(style); } // Check if the token and domain list are set function checkInitialization() { const token = GM_getValue('api_token', ''); if (!token) { console.log('API token not found. Attempting to fetch...'); updatetoken(); } if (targetURLs.length === 0) { console.log('Domain list is empty. Fetching domains...'); updateDDLDomains(); } ensureUpdateDDLDomains(); } const processedURLs = new Set(); // Track processed URLs // Check if the current URL contains any of the target URLs function isTargetURL() { const currentUrl = window.location.href; return targetURLs.some(target => currentUrl.includes(target)); } // Generate a regex pattern based on targetURLs function generateUrlRegex() { const domains = targetURLs.map(url => url.replace(/\./g, '\\.') + '[^\s"]*'); const pattern = `https:\\/\\/(?:${domains.join('|')})`; return new RegExp(pattern, 'g'); } function createFastDownloadButton(linkElement, fileURL) { let button = document.createElement('button'); button.innerHTML = 'Send to RD'; button.style.marginLeft = '5px'; // Add space to the left of the button button.style.padding = '2px 5px'; // Add space to the right of the button button.style.backgroundColor = 'black'; // Set button background color to red button.style.color = 'white'; // Optional: Set button text color to white for better contrast button.style.borderRadius = '5px'; // Add rounded corners button.onclick = () => { GM.xmlHttpRequest({ method: 'POST', url: 'https://app.real-debrid.com/rest/1.0/unrestrict/link', headers: { "authorization": `Bearer ${token}` }, data: `link=${encodeURIComponent(fileURL)}&password=`, onload: (response) => { const jsonData = JSON.parse(response.responseText); if (jsonData.download !== undefined) { linkElement.href = jsonData.download; linkElement.textContent = jsonData.filename; } else { linkElement.textContent += ' - failed'; } button.remove(); } }); }; linkElement.setAttribute('realdebrid', 'true'); linkElement.insertAdjacentElement('afterend', button); } // Create a button for magnet links function createMagnetButton(linkElement, fileURL) { let button = document.createElement('button'); button.innerHTML = 'Send Magnet to RD'; button.style.marginLeft = '5px'; button.style.padding = '2px 5px'; button.style.backgroundColor = 'green'; button.style.color = 'white'; button.style.borderRadius = '5px'; button.onclick = () => { GM.xmlHttpRequest({ method: 'POST', url: 'https://api.real-debrid.com/rest/1.0/torrents/addMagnet', headers: { "authorization": `Bearer ${token}` }, data: `magnet=${encodeURIComponent(fileURL)}`, onload: (response) => { const jsonData = JSON.parse(response.responseText); if (jsonData.id) { showToast('Magnet successfully added to Real-Debrid.'); } else { showToast('Failed to add magnet link.'); } button.remove(); } }); }; linkElement.setAttribute('realdebrid-magnet', 'true'); linkElement.insertAdjacentElement('afterend', button); } function processTextNode(textNode) { const textContent = textNode.textContent; const urlRegex = generateUrlRegex(); let match; while ((match = urlRegex.exec(textContent)) !== null) { const url = match[0]; if (processedURLs.has(url)) { continue; // Skip URLs that have already been processed } for (let targetURL of targetURLs) { if (url.includes(targetURL)) { const linkElement = document.createElement('a'); linkElement.href = url; linkElement.textContent = url; linkElement.style.display = 'block'; // Ensure each link appears on a new line textNode.parentNode.insertBefore(linkElement, textNode); createFastDownloadButton(linkElement, url); processedURLs.add(url); // Mark URL as processed } } } } function processPreElements() { let preElements = document.getElementsByTagName('pre'); for (let i = 0; i < preElements.length; i++) { let preElement = preElements[i]; let textNodes = Array.from(preElement.childNodes).filter(node => node.nodeType === Node.TEXT_NODE); textNodes.forEach(node => processTextNode(node)); } } // Process links function processLinks() { const links = document.querySelectorAll('a[href]'); links.forEach(link => { const href = link.href; if (href.startsWith('magnet:?')) { if (!link.hasAttribute('realdebrid-magnet')) { createMagnetButton(link, href); } } else if (targetURLs.some(domain => href.includes(domain))) { if (!link.hasAttribute('realdebrid')) { createFastDownloadButton(link, href); } } }); } function observeLinks() { processLinks(); if (!isTargetURL()) { // Process anchor tags let links = document.getElementsByTagName('a'); for (let i = 0; i < links.length; i++) { let link = links[i]; if (link.getAttribute('realdebrid')) { continue; } for (let targetURL of targetURLs) { if (link.href.includes(targetURL) && !processedURLs.has(link.href)) { createFastDownloadButton(link, link.href); processedURLs.add(link.href); // Mark URL as processed } } } // Process text nodes in <pre> elements processPreElements(); // Process text nodes in the rest of the document const iterator = document.createNodeIterator( document.body, NodeFilter.SHOW_TEXT, null, false ); let node; while ((node = iterator.nextNode())) { if (node.parentNode.tagName !== 'PRE' && !node.parentNode.querySelector('a[realdebrid]')) { // Skip nodes with processed children processTextNode(node); } } }} // Debounce function to limit how often observeLinks is called function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Observe the document body for changes const observer = new MutationObserver(debounce(observeLinks, 500)); observer.observe(document.body, { childList: true, subtree: true }); // Initialize the script when the page loads window.onload = () => { createMenu(); // Register the menu commands checkInitialization(); // Ensure token and domain list are set observeLinks; //observe the links when the page first loads };