您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download files with a single click without visiting the debrid providers' website.
// ==UserScript== // @name Debrid Download Helper (Multi-Provider) // @namespace http://tampermonkey.net/ // @version 1.12 // @author Superflyin // @match *://*/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @require https://code.jquery.com/jquery-3.6.0.min.js // @connect api.real-debrid.com // @connect api.alldebrid.com // @connect www.premiumize.me // @connect api.linksnappy.com // @connect api.torbox.io // @description Download files with a single click without visiting the debrid providers' website. // ==/UserScript== (function($) { 'use strict'; console.log('Debrid Download Helper: Script started! (v26.5)'); // IMPORTANT: Ensure the script only runs in the top-level window (not in iframes) if (window.top !== window.self) { console.log('Debrid Download Helper: Script detected running in an iframe. Exiting.'); return; // Exit if we are in an iframe } // --- Provider configuration --- const providers = { "real-debrid": { name: "Real-Debrid", icon: "https://i.ibb.co/6RW9YRnw/realdebrid-116047.webp", // Updated icon URL apiUrl: "https://api.real-debrid.com/rest/1.0/unrestrict/link", apiMagnetUrl: "https://api.real-debrid.com/rest/1.0/torrents/addMagnet", method: "POST", headers: apiKey => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/x-www-form-urlencoded' }), data: url => `link=${encodeURIComponent(url)}`, magnetData: magnetUrl => `magnet=${encodeURIComponent(magnetUrl)}`, parseResponse: resp => { // For direct links try { return JSON.parse(resp).download || null; } catch { return null; } }, parseMagnetResponse: resp => { // For magnet links try { const data = JSON.parse(resp); return data && data.id ? true : false; // Return true on successful ID, then user goes to torrents page } catch (e) { return false; } } }, "alldebrid": { name: "AllDebrid", icon: "https://addons.mozilla.org/user-media/addon_icons/662/662954-64.png?modified=1590413336", apiUrl: "https://api.alldebrid.com/v4/link/unlock", apiMagnetUrl: "https://api.alldebrid.com/v4/magnet/upload", method: "GET", // AllDebrid link unlock uses GET headers: () => ({}), // No custom headers for AllDebrid GET buildUrl: (apiKey, url) => `https://api.alldebrid.com/v4/link/unlock?agent=userscript&apikey=${apiKey}&link=${encodeURIComponent(url)}`, parseResponse: resp => { try { let j = JSON.parse(resp); return (j.data && j.data.link) || null; } catch { return null; } } }, "premiumize": { name: "Premiumize", icon: "https://www.premiumize.me/favicon.ico", apiUrl: "https://www.premiumize.me/api/unrestrict/link", method: "GET", headers: () => ({}), buildUrl: (apiKey, url) => `https://www.premiumize.me/api/unrestrict/link?link=${encodeURIComponent(url)}&auth=${apiKey}`, parseResponse: resp => { try { let j = JSON.parse(resp); return (j.status === "success" && j.download) || null; } catch { return null; } } }, "linksnappy": { name: "LinkSnappy", icon: "https://lh3.googleusercontent.com/pCVuXSx1pjsSR5Kzh98zJmRKiB1e1b_p7uKJ7s-5YQ8lpnO7SHtYksHcGpuvoxX4j5ZyVaF31URB3sbHMteygmyF=s120", apiUrl: "https://api.linksnappy.com/file/unlock", method: "POST", headers: apiKey => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/x-www-form-urlencoded' }), data: url => `url=${encodeURIComponent(url)}`, parseResponse: resp => { try { let j = JSON.parse(resp); return (j.result && j.result.url) || null; } catch { return null; } } }, "torbox": { name: "TorBox", icon: "https://downloads.intercomcdn.com/i/o/570645/98aded70c10f217bd63035f7/80582a6c13665edcd8ddb65505b883fb.png", // Updated TorBox icon link apiUrl: "https://api.torbox.io/v1/unrestrict", method: "POST", headers: apiKey => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/x-www-form-urlencoded' }), data: url => `link=${encodeURIComponent(url)}`, parseResponse: resp => { try { return JSON.parse(resp).download || null; } catch { return null; } } } }; // --- Supported hosts and file link patterns (for direct links only) --- const supportedPatterns = [ { host: "1fichier.com", pattern: /^\/\?[a-zA-Z0-9]{20,}\/?$/ }, { host: "dailyuploads.net", pattern: /^\/[a-zA-Z0-9]{12,}$/ }, { host: "ddownload.com", pattern: /^\/[a-zA-Z0-9]{10,}/ }, { host: "dropbox.com", pattern: /^\/s\/[a-zA-Z0-9]+\/.+/ }, { host: "filefactory.com", pattern: /^\/file\/[a-zA-Z0-9]+/ }, { host: "filenext.com", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "filespace.com", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "gigapeta.com", pattern: /^\/file\/[a-zA-Z0-9]+/ }, { host: "hitfile.net", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "katfile.com", pattern: /^\/(file\/)?(?:view\/)?[a-zA-Z0-9]{10,}(\/.*)?$/ }, { host: "mediafire.com", pattern: /^\/file\/[a-zA-Z0-9_]+\/.+/ }, { host: "mega.nz", pattern: /^\/file\/[a-zA-Z0-9!#]+/ }, { host: "nitroflare.com", pattern: /^\/view\/[A-Za-z0-9]+(\/[^\/]+)?$/ }, { host: "prefiles.com", pattern: /^\/f\/[a-z0-9]+\/?/ }, { host: "rapidgator.net", pattern: /^\/file\/[a-fA-F0-9]{32}/ }, { host: "turbobit.net", pattern: /[a-z0-9]{15,}\.html$/ }, { host: "uploady.io", pattern: /^\/[A-Za-z0-9]{12,}/ }, { host: "userscloud.com", pattern: /^\/[a-z0-9]{12,}$/ }, { host: "usersdrive.com", pattern: /^\/file\/[a-z0-9]+/ }, { host: "userupload.net", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "vidoza.net", pattern: /^\/[a-z0-9]{12,}/ }, { host: "vipfile.com", pattern: /^\/d\/[a-z0-9]+\/?/ }, { host: "worldbytez.com", pattern: /^\/[a-z0-9]+\.html$/ }, { host: "wupfile.com", pattern: /^\/file\/[a-z0-9]+/ }, // Added hosts from new hosts.txt with common patterns (adjust if specific patterns are known) { host: "4shared.com", pattern: /^\/file\/.+/ }, { host: "alfafile.net", pattern: /^\/file\/.+/ }, { host: "apkadmin.com", pattern: /^\/file\/.+/ }, { host: "clicknupload.cc", pattern: /^\/file\/.+/ }, { host: "cloudvideo.tv", pattern: /^\/[a-zA-Z0-9]{12,}$/ }, { host: "drop.download", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "dropgalaxy.com", pattern: /^\/file\/.+/ }, { host: "exload.pro", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "fastbit.cc", pattern: /^\/file\/.+/ }, { host: "fikper.com", pattern: /^\/file\/.+/ }, { host: "file.al", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "filedot.xyz", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "filerio.in", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "filestore.to", pattern: /^\/file\/.+/ }, { host: "filextras.com", pattern: /^\/file\/.+/ }, { host: "filezip.cc", pattern: /^\/file\/.+/ }, { host: "flashbit.cc", pattern: /^\/file\/.+/ }, { host: "hexupload.net", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "hot4share.com", pattern: /^\/file\/.+/ }, { host: "indishare.org", pattern: /^\/file\/.+/ }, { host: "isra.cloud", pattern: /^\/file\/.+/ }, { host: "mexashare.com", pattern: /^\/file\/.+/ }, { host: "mixdrop.ag", pattern: /^\/f\/.+/ }, { host: "modsbase.com", pattern: /^\/download\/.+/ }, { host: "mp4upload.com", pattern: /^\/embed-[a-zA-Z0-9]+\.html/ }, { host: "send.cm", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "sendit.cloud", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "sendspace.com", pattern: /^\/file\/.+/ }, { host: "sharemods.com", pattern: /^\/download\/.+/ }, { host: "simfileshare.net", pattern: /^\/download\/.+/ }, { host: "terabytez.net", pattern: /^\/file\/.+/ }, { host: "turbobit.net", pattern: /[a-z0-9]{15,}\.html$/ }, { host: "upload42.com", pattern: /^\/file\/.+/ }, { host: "uploadbank.com", pattern: /^\/file\/.+/ }, { host: "uploadbox.com", pattern: /^\/file\/.+/ }, { host: "uploadboy.com", pattern: /^\/file\/.+/ }, { host: "uploadev.org", pattern: /^\/[a-zA-Z0-9]{12,}/ }, { host: "uploadrar.com", pattern: /^\/file\/.+/ }, { host: "voe.sx", pattern: /^\/e\/.+/ }, { host: "wayupload.com", pattern: /^\/file\/.+/ }, { host: "wipfiles.net", pattern: /^\/file\/.+/ } ]; // Function to check if a URL is a magnet link OR a supported direct file link function isSupportedLink(url) { if (url.startsWith('magnet:?')) { return true; } try { const parsedUrl = new URL(url); const host = parsedUrl.hostname.replace(/^www\./, ''); const fullPathForMatch = parsedUrl.pathname + parsedUrl.search; const isMatch = supportedPatterns.some(h => { const hostMatch = h.host === host; const patternMatch = h.pattern.test(fullPathForMatch); return hostMatch && patternMatch; }); return isMatch; } catch (e) { console.log(`Debrid Download Helper: Skipping invalid URL: ${href}`); return false; // Invalid URL } } // Function to check if the current page's URL matches a supported file hoster page (for top bar display) function isFileHosterPage(url) { try { const parsedUrl = new URL(url); const host = parsedUrl.hostname.replace(/^www\./, ''); const fullPathForMatch = parsedUrl.pathname + parsedUrl.search; const isMatch = supportedPatterns.some(h => h.host === host && h.pattern.test(fullPathForMatch)); console.log(`Debrid Download Helper (v${GM_info.script.version}): isFileHosterPage(${url}) -> ${isMatch}. Host: ${host}, Full path for check: ${fullPathForMatch}`); return isMatch; } catch (e) { console.error(`Debrid Download Helper (v${GM_info.script.version}): Error in isFileHosterPage for URL ${url}`, e); return false; } } // --- Settings --- let currentProvider = GM_getValue('currentProvider', 'real-debrid'); let apiKeys = GM_getValue('debridApiKeys', {}); let apiKey = apiKeys[currentProvider] || ''; // --- Provider selection menu --- function showProviderMenu(event) { $('#debrid-provider-menu').remove(); // Remove any existing menu const $menu = $('<div id="debrid-provider-menu"></div>').css({ position: 'fixed', // Adjusted positioning for mobile-friendliness top: '50%', left: '50%', transform: 'translate(-50%, -50%)', // Center both horizontally and vertically background: '#222', border: '1px solid #555', padding: '8px 12px', borderRadius: '6px', zIndex: 999999, // Ensure it's on top display: 'flex', flexWrap: 'wrap', // Allow items to wrap on smaller screens justifyContent: 'center', // Center buttons horizontally gap: '12px', boxShadow: '0 0 15px rgba(0,0,0,0.8)', color: '#eee', fontFamily: 'Arial, sans-serif', fontSize: '14px', userSelect: 'none', maxWidth: '90%', // Limit width on mobile boxSizing: 'border-box' // Include padding in width }); Object.entries(providers).forEach(([key, provider]) => { const isSelected = (key === currentProvider); const $btn = $('<button></button>').css({ background: isSelected ? '#4CAF50' : 'transparent', border: isSelected ? '2px solid #4CAF50' : '1px solid #555', cursor: 'pointer', padding: '10px 15px', // Increased padding for touch targets borderRadius: '5px', display: 'flex', flexDirection: 'column', // Stack icon and text vertically alignItems: 'center', gap: '5px', // Reduced gap for stacked elements color: isSelected ? '#fff' : '#ccc', fontWeight: isSelected ? 'bold' : 'normal', transition: 'background-color 0.3s, border-color 0.3s', minWidth: '100px' // Ensure buttons are large enough for touch }).attr('title', provider.name); $btn.append($(`<img src="${provider.icon}" width="32" height="32" alt="${provider.name} icon">`).css({ flexShrink: 0 })); // Slightly larger icons for touch $btn.append($('<span></span>').text(provider.name)); $btn.on('click', () => { currentProvider = key; GM_setValue('currentProvider', currentProvider); apiKey = apiKeys[currentProvider] || ''; $menu.remove(); console.log(`Debrid Download Helper: Provider switched to ${currentProvider}. Reloading page.`); location.reload(); // Auto page refresh implemented here }); $menu.append($btn); }); $('body').append($menu); // Close menu on outside click $(document).on('click.debridMenu', (e) => { if (!$(e.target).closest('#debrid-provider-menu').length) { $menu.remove(); $(document).off('click.debridMenu'); // Deregister handler } }); } // --- API Key Modal --- function showApiKeyModal(providerName, currentKey, callback) { $('#debrid-apikey-modal-overlay').remove(); // Remove existing if any const $overlay = $('<div id="debrid-apikey-modal-overlay"></div>').css({ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', background: 'rgba(0, 0, 0, 0.7)', zIndex: 999998, // Below provider menu, above everything else display: 'flex', alignItems: 'center', justifyContent: 'center' }); const $modal = $('<div id="debrid-apikey-modal"></div>').css({ background: '#333', border: '1px solid #666', borderRadius: '8px', padding: '25px', boxShadow: '0 0 20px rgba(0,0,0,0.9)', color: '#eee', fontFamily: 'Arial, sans-serif', fontSize: '15px', textAlign: 'center', maxWidth: '90%', // Make modal responsive width: '400px', // Max width for larger screens boxSizing: 'border-box' }); $modal.html(` <h3 style="margin-top: 0; color: #4CAF50;">${providerName} API Key Required</h3> <p style="margin-bottom: 20px;">Please enter your API key for ${providerName}.</p> <input type="text" id="debrid-api-key-input" value="${currentKey || ''}" placeholder="Enter API Key here" style=" width: calc(100% - 20px); padding: 12px; /* Increased padding for touch */ margin-bottom: 15px; border: 1px solid #555; border-radius: 4px; background: #444; color: #eee; font-size: 1.1em; /* Slightly larger font */ "> <button id="debrid-api-key-save" style=" background: #4CAF50; color: white; border: none; padding: 12px 25px; /* Increased padding for touch */ border-radius: 5px; cursor: pointer; font-size: 1.1em; /* Slightly larger font */ margin-right: 10px; transition: background-color 0.2s; ">Save</button> <button id="debrid-api-key-cancel" style=" background: #777; color: white; border: none; padding: 12px 25px; /* Increased padding for touch */ border-radius: 5px; cursor: pointer; font-size: 1.1em; /* Slightly larger font */ transition: background-color 0.2s; ">Cancel</button> `); $('body').append($overlay.append($modal)); $('#debrid-api-key-input').focus(); $('#debrid-api-key-save').on('click', () => { const newKey = $('#debrid-api-key-input').val().trim(); apiKeys[currentProvider] = newKey; GM_setValue('debridApiKeys', apiKeys); apiKey = newKey; $overlay.remove(); if (callback) callback(true); // Indicate key was saved }).hover(function() { $(this).css('background-color', '#5cb85c'); }, function() { $(this).css('background-color', '#4CAF50'); }); $('#debrid-api-key-cancel').on('click', () => { $overlay.remove(); if (callback) callback(false); // Indicate user cancelled }).hover(function() { $(this).css('background-color', '#888'); }, function() { $(this).css('background-color', '#777'); }); // Allow closing with Escape key $(document).on('keydown.debridApiModal', function(e) { if (e.key === "Escape") { $overlay.remove(); $(document).off('keydown.debridApiModal'); if (callback) callback(false); } }); } // --- Tampermonkey menu commands --- GM_registerMenuCommand("Switch Debrid Provider", () => { // When triggered from menu, pass event object for potential positioning, // though our updated CSS centers it regardless of event clientX/Y showProviderMenu(event); }); GM_registerMenuCommand("Set Debrid API Key (current provider)", () => { showApiKeyModal(providers[currentProvider].name, apiKeys[currentProvider] || ''); }); // Create a debrid download button (icon only, transparent background, sized, placed after hosting link) function createDebridButton(url, $link) { const provider = providers[currentProvider]; // Base width/height for all icons const baseIconWidth = 18; const baseIconHeight = 18; let imgHtml = `<img src="${provider.icon}" width="${baseIconWidth}" height="${baseIconHeight}" alt="${provider.name}">`; let inlineStyle = ""; // Apply specific styles if (currentProvider === "real-debrid") { inlineStyle += "opacity: 0.8 !important;"; // 20% transparent (80% opacity) } else if (currentProvider === "alldebrid") { // Make AllDebrid icon a bit bigger (e.g., 20x20px) inlineStyle += `width: 20px !important; height: 20px !important;`; } // Apply inline style if any if (inlineStyle) { imgHtml = `<img src="${provider.icon}" width="${baseIconWidth}" height="${baseIconHeight}" alt="${provider.name}" style="${inlineStyle}">`; } const $btn = $('<button>') .addClass('debrid-btn') .attr('title', `Download with ${provider.name}`) .html(imgHtml) // Use the constructed imgHtml .on('click', function(e) { // If Alt key is pressed, show provider menu instead of downloading if (e.altKey) { e.preventDefault(); e.stopPropagation(); // Stop propagation to prevent default link click showProviderMenu(e); return false; } handleDebridDownload(url, $(this)); }); $link.after($btn); $link.data('debrid-btn-added', true); // Mark link as processed return $btn; } // Add debrid buttons next to supported links and magnet links function addIconsToLinks(context = document) { let buttonsAddedCount = 0; $(context).find('a[href]').each(function() { const $link = $(this); const href = $link.attr('href'); // Skip if already processed, no href, or internal fragment link if ($link.data('debrid-btn-added') || !href || href.startsWith('#')) { return; } // Resolve relative URLs let fullUrl; try { fullUrl = new URL(href, window.location.href).href; } catch (e) { console.log(`Debrid Download Helper: Skipping invalid URL: ${href}`); return; // Invalid URL } if (isSupportedLink(fullUrl)) { createDebridButton(fullUrl, $link); buttonsAddedCount++; } }); } // --- Add top download bar on supported file pages --- function addDownloadBar() { console.log(`Debrid Download Helper: Running addDownloadBar`); // Remove existing bar if any, to prevent duplicates $('#debrid-download-bar').remove(); const $bar = $(` <div id="debrid-download-bar"> <span>Download this file with ${providers[currentProvider].name}:</span> <button class="debrid-bar-action-button" title="Download current page with ${providers[currentProvider].name}"> <img src="${providers[currentProvider].icon}" alt="${providers[currentProvider].name}" width="22" height="22"> </button> </div> `); $bar.find('.debrid-bar-action-button').on('click', function(e) { e.preventDefault(); handleDebridDownload(location.href, $(this)); }); $('body').prepend($bar); console.log(`Debrid Download Helper: Bar appended to body.`); } // --- Handle the display logic based on the current URL --- function refreshDisplayBasedOnUrl() { console.log(`Debrid Download Helper: Running refreshDisplayBasedOnUrl for URL:`, location.href); const currentPageIsFileHoster = isFileHosterPage(location.href); // Remove all previous indicators to ensure clean state before re-rendering $('#debrid-download-bar').remove(); $('.debrid-btn').remove(); // Clear the data attribute from ALL links that might have been processed // This is crucial for re-processing elements on DOM changes or provider switch $('a[data-debrid-btn-added="true"]').each(function() { $(this).removeData('debrid-btn-added'); }); if (currentPageIsFileHoster) { // On a file hoster page, only show the top bar addDownloadBar(); // Disconnect MutationObserver as it's not needed for static file pages observer.disconnect(); console.log(`Debrid Download Helper: Current page is file hoster. Displaying download bar.`); } else { // On other pages, show icons next to links and observe for new content addIconsToLinks(); // Use MutationObserver to add icons to dynamically loaded content observer.observe(document.body, { childList: true, subtree: true }); console.log(`Debrid Download Helper: Current page is NOT file hoster. Displaying link icons.`); } } // --- Main Download Handler for direct links and magnets --- function handleDebridDownload(url, $button) { // Check for API key *before* attempting download if (!apiKey) { showApiKeyModal(providers[currentProvider].name, '', (keySaved) => { if (keySaved && apiKey) { // If key was saved successfully, try download again handleDebridDownload(url, $button); } else { $button.prop('disabled', false); // Re-enable button if cancelled or not saved $button.html($button.data('original-content')); // Restore original icon } }); return; // Stop current download attempt } const provider = providers[currentProvider]; $button.prop('disabled', true); // Store original content (either img tag or plain text like '✔️') $button.data('original-content', $button.html()); // Use a <span> for the loading spinner and checkmark to avoid issues with img src $button.html('<span class="debrid-loader"></span>'); // This will be the spinner // Function to show checkmark and keep it permanent function showCheckmarkPermanent() { $button.html('<span class="debrid-checkmark">✔️</span>'); // Checkmark emoji $button.prop('disabled', true); // Keep button disabled after successful download // No setTimeout to revert, so it stays } const isMagnet = url.startsWith('magnet:?'); if (isMagnet) { // --- Magnet Link Handling --- if (!provider.apiMagnetUrl) { alert(`Magnet support is not implemented for ${provider.name} in this script version.`); $button.prop('disabled', false); $button.html($button.data('original-content')); return; } let magnetReqUrl, magnetReqMethod, magnetReqHeaders, magnetReqData; if (currentProvider === "real-debrid") { magnetReqUrl = provider.apiMagnetUrl; magnetReqMethod = provider.method; // POST magnetReqHeaders = provider.headers(apiKey); magnetReqData = provider.magnetData(url); } else if (currentProvider === "alldebrid") { // AllDebrid magnet upload is a GET request with magnet in URL params magnetReqUrl = `https://api.alldebrid.com/v4/magnet/upload?agent=userscript&apikey=${apiKey}&magnet=${encodeURIComponent(url)}`; magnetReqMethod = "GET"; magnetReqHeaders = {}; magnetReqData = null; } else { alert(`Magnet support is not implemented for ${provider.name} in this script version.`); $button.prop('disabled', false); $button.html($button.data('original-content')); return; } GM_xmlhttpRequest({ method: magnetReqMethod, url: magnetReqUrl, headers: magnetReqHeaders, data: magnetReqData, onload: function(response) { console.log(`Debrid Download Helper: API Response for magnet link (${currentProvider}):`, response.responseText); try { const data = JSON.parse(response.responseText); let success = false; if (currentProvider === "real-debrid") { if (provider.parseMagnetResponse(response.responseText)) { console.log("Debrid Download Helper: Real-Debrid magnet added successfully."); window.open("https://real-debrid.com/torrents", "_blank"); success = true; } else { alert("Failed to add magnet link to Real-Debrid. Check your API key and magnet link."); console.error(`Debrid Download Helper: Real-Debrid magnet add failed. Response:`, response.responseText); } } else if (currentProvider === "alldebrid") { if (data.status === "success" && data.data.magnets && data.data.magnets.length > 0) { const magnet = data.data.magnets[0]; console.log("Debrid Download Helper: AllDebrid magnet data:", magnet); if (magnet.ready && magnet.links && magnet.links.length > 0) { const downloadUrl = magnet.links[0]; console.log("Debrid Download Helper: AllDebrid magnet direct download URL:", downloadUrl); const a = document.createElement('a'); a.href = downloadUrl; a.download = ''; a.style.display = 'none'; document.body.appendChild(a); a.click(); setTimeout(() => document.body.removeChild(a), 1000); success = true; } else if (magnet.id) { console.log("Debrid Download Helper: AllDebrid magnet processing. Opening torrents page."); window.open(`https://alldebrid.com/magnets/?id=${magnet.id}`, "_blank"); success = true; } else { alert("Magnet added to AllDebrid, but not ready yet. Check your magnets page manually."); console.warn("Debrid Download Helper: AllDebrid magnet added but no direct link or ID for opening page.", response.responseText); } } else { alert("Failed to add magnet link to AllDebrid. Check your API key and magnet link."); console.error(`Debrid Download Helper: AllDebrid magnet add failed. Response:`, response.responseText); } } if (success) { showCheckmarkPermanent(); // Changed to permanent checkmark } else { $button.prop('disabled', false); $button.html($button.data('original-content')); } } catch (e) { alert(`Error parsing response from ${provider.name} for magnet link: ` + e.message); console.error(`Debrid Download Helper: Error parsing magnet response from ${provider.name}:`, e, 'Response:', response.responseText); $button.prop('disabled', false); $button.html($button.data('original-content')); } }, onerror: function(response) { alert(`Network error contacting ${provider.name} for magnet link.`); console.error(`Debrid Download Helper: Network error for magnet link with ${provider.name}:`, response); $button.prop('disabled', false); $button.html($button.data('original-content')); } }); } else { // --- Direct Link Handling --- let reqUrl, reqOpts; if (provider.method === "GET") { reqUrl = provider.buildUrl(apiKey, url); reqOpts = { method: "GET", url: reqUrl, headers: provider.headers(apiKey) }; } else { // POST method reqUrl = provider.apiUrl; reqOpts = { method: provider.method, url: reqUrl, headers: provider.headers(apiKey), data: provider.data(url) }; } GM_xmlhttpRequest({ method: reqOpts.method, url: reqOpts.url, headers: reqOpts.headers, data: reqOpts.data, onload: function(response) { console.log(`Debrid Download Helper: API Response for direct link (${currentProvider}):`, response.responseText); try { const downloadUrl = provider.parseResponse(response.responseText); console.log(`Debrid Download Helper: Parsed Download URL:`, downloadUrl); if (downloadUrl) { const a = document.createElement('a'); a.href = downloadUrl; a.download = ''; a.style.display = 'none'; document.body.appendChild(a); a.click(); setTimeout(() => document.body.removeChild(a), 1000); showCheckmarkPermanent(); // Changed to permanent checkmark } else { alert('Failed to get direct download link. Check your API key and link, or the file might be offline.'); console.error(`Debrid Download Helper: Failed to parse download URL from response:`, response.responseText); $button.prop('disabled', false); $button.html($button.data('original-content')); // Revert on failure } } catch (e) { alert('Error parsing response from debrid provider for direct link.'); console.error('Debrid Download Helper: Error during response parsing:', e, 'Response:', response.responseText); $button.prop('disabled', false); $button.html($button.data('original-content')); // Revert on failure } }, onerror: function(response) { alert('Network error contacting debrid provider for direct link.'); console.error('Debrid Download Helper: Network error for direct link:', response); $button.prop('disabled', false); $button.html($button.data('original-content')); // Revert on network error } }); } } // --- MutationObserver for dynamic content (only active on non-file-hoster pages) --- const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { // Element node addIconsToLinks(node); } }); } }); }); // --- Init --- $(document).ready(function() { console.log(`Debrid Download Helper (v${GM_info.script.version}): Document ready. Initializing script.`); refreshDisplayBasedOnUrl(); // Initial check and display GM_addStyle(` /* General button/icon styles */ .debrid-btn { background: transparent !important; border: none !important; padding: 0 !important; margin: 0 0 0 5px !important; cursor: pointer !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; width: 22px !important; height: 22px !important; vertical-align: middle !important; box-sizing: border-box !important; } .debrid-btn img { display: block !important; width: 18px !important; height: 18px !important; opacity: 1 !important; /* Base opacity, overridden by inline style for RD and Alldebrid */ transition: opacity 0.2s !important; border-radius: 3px !important; } .debrid-btn:hover img { opacity: 1 !important; /* Remain fully visible on hover */ } /* Spinner and Checkmark styles */ .debrid-loader { display: inline-block; width: 18px; height: 18px; border: 2px solid #f3f3f3; /* Light grey */ border-top: 2px solid #4CAF50; /* Green */ border-radius: 50%; animation: spin 1s linear infinite; } .debrid-checkmark { font-size: 16.2px; /* 10% smaller than 18px */ line-height: 1; /* Ensure vertical alignment */ color: #4CAF50; /* Green color for checkmark */ opacity: 0.8; /* 20% transparent */ } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Provider menu styles */ #debrid-provider-menu { /* Existing styles */ /* Overrides for mobile responsiveness */ padding: 15px !important; /* More padding for touch */ max-width: 90vw !important; /* Max width 90% of viewport width */ min-width: 280px; /* Minimum width to ensure readability */ box-sizing: border-box; /* Include padding in width calculation */ } #debrid-provider-menu button { width: calc(50% - 12px); /* Two buttons per row with gap */ margin: 6px; /* Spacing between buttons */ } @media (max-width: 480px) { /* Adjust for very small screens if needed */ #debrid-provider-menu button { width: calc(100% - 12px); /* One button per row on very small screens */ } } /* Download bar styles */ #debrid-download-bar { position: fixed; top: 0; left: 50%; transform: translateX(-50%); background: #282c34; color: white; padding: 8px 15px; border-radius: 0 0 8px 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.3); z-index: 100000; display: flex; align-items: center; gap: 10px; font-family: Arial, sans-serif; font-size: 14px; max-width: 95vw; /* Limit width for mobile */ box-sizing: border-box; } #debrid-download-bar .debrid-bar-action-button { background: transparent; border: none; padding: 0; cursor: pointer; } #debrid-download-bar .debrid-bar-action-button img { width: 22px; height: 22px; vertical-align: middle; opacity: 1 !important; /* MADE FULLY OPAQUE AS REQUESTED */ border-radius: 3px; /* Removed transition and hover opacity rule for simplicity since it's always 1 now */ } /* API Key Modal Styles */ #debrid-apikey-modal-overlay { /* Styles defined inline in showApiKeyModal for dynamic positioning */ } #debrid-apikey-modal { /* Styles defined inline in showApiKeyModal for dynamic positioning */ } #debrid-apikey-modal button:hover { filter: brightness(1.1); /* Slight brightness change on hover */ } /* Specific styles for mobile modal input/buttons */ #debrid-api-key-input { font-size: 1.1em !important; padding: 12px !important; } #debrid-apikey-modal button { font-size: 1.1em !important; padding: 12px 25px !important; } `); }); })(jQuery);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址