Last active
April 24, 2026 12:27
-
-
Save motebaya/baf74af2cfd1391b7725be2de3958911 to your computer and use it in GitHub Desktop.
TamperMonkey: MediaFire Bulk Download with auto-download flow and aggressive popup prevention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name MediaFire Bulk download | |
| // @namespace http://tampermonkey.net/ | |
| // @version 2.1 | |
| // @description Adds a DOWNLOAD ALL button on MediaFire folder pages and automatically triggers downloads on file pages while blocking popup/ads. | |
| // @author @github.com/motebaya | |
| // @license MIT | |
| // @icon https://www.mediafire.com/favicon.ico | |
| // @match https://www.mediafire.com/folder/* | |
| // @match http://www.mediafire.com/folder/* | |
| // @match https://www.mediafire.com/file* | |
| // @match http://www.mediafire.com/file* | |
| // @match https://www.mediafire.com/file_premium* | |
| // @match http://www.mediafire.com/file_premium* | |
| // @grant none | |
| // @run-at document-start | |
| // ==/UserScript== | |
| (function () { | |
| "use strict"; | |
| const href = location.href; | |
| const isFolderPage = /:\/\/(?:www\.)?mediafire\.com\/folder\//i.test(href); | |
| const isFilePage = /:\/\/(?:www\.)?mediafire\.com\/file(?:_premium)?/i.test( | |
| href, | |
| ); | |
| console.log("[MF] Script loaded:", href); | |
| function sleep(ms) { | |
| return new Promise((resolve) => setTimeout(resolve, ms)); | |
| } | |
| /** | |
| * Displays a flash notification message on the page. | |
| * @param {string} [message="TEST"] - The message to display. | |
| * @param {string} [type="success"] - The type of notification, either "success" or "error". | |
| * @param {number} [duration=2200] - The duration in milliseconds for which the notification is shown. | |
| */ | |
| async function showFlash( | |
| message = "TEST", | |
| type = "success", | |
| duration = 2200, | |
| ) { | |
| let root = document.getElementById("mf-flash-root"); | |
| if (!root) { | |
| root = document.createElement("div"); | |
| root.id = "mf-flash-root"; | |
| root.style.cssText = [ | |
| "position: fixed", | |
| "top: 16px", | |
| "left: 50%", | |
| "transform: translateX(-50%)", | |
| "z-index: 2147483647", | |
| "pointer-events: none", | |
| "display: flex", | |
| "flex-direction: column", | |
| "align-items: center", | |
| ].join(";"); | |
| document.documentElement.appendChild(root); | |
| } | |
| const notif = document.createElement("div"); | |
| notif.textContent = message; | |
| const isError = type === "error"; | |
| notif.style.cssText = [ | |
| "margin-top: 10px", | |
| "padding: 12px 18px", | |
| "border-radius: 12px", | |
| "color: #fff", | |
| "font: 600 14px/1.4 Arial, sans-serif", | |
| "box-shadow: 0 10px 30px rgba(0,0,0,.25)", | |
| "opacity: 0", | |
| "transform: translateY(-10px)", | |
| "transition: opacity .25s ease, transform .25s ease", | |
| "pointer-events: none", | |
| "background: " + (isError ? "#d93025" : "#0050c9"), | |
| "max-width: 90vw", | |
| "white-space: nowrap", | |
| ].join(";"); | |
| root.appendChild(notif); | |
| requestAnimationFrame(() => { | |
| notif.style.opacity = "1"; | |
| notif.style.transform = "translateY(0)"; | |
| }); | |
| await sleep(duration - 300); | |
| notif.style.opacity = "0"; | |
| notif.style.transform = "translateY(-10px)"; | |
| await sleep(duration); | |
| notif.remove(); | |
| if (!root.children.length) root.remove(); | |
| } | |
| /** | |
| * file page handle - block popups/ads and auto click download button | |
| */ | |
| if (isFilePage) { | |
| const originalOpen = window.open; | |
| window.open = function (url, windowName, windowFeatures) { | |
| showFlash("[POPUP BLOCKED] window.open: " + url, "success"); | |
| return { | |
| closed: true, | |
| close: function () {}, | |
| focus: function () {}, | |
| blur: function () {}, | |
| }; | |
| }; | |
| window.showModalDialog = function () { | |
| showFlash("[POPUP BLOCKED] showModalDialog", "success"); | |
| return null; | |
| }; | |
| window.showModelessDialog = function () { | |
| showFlash("[POPUP BLOCKED] showModelessDialog", "success"); | |
| return null; | |
| }; | |
| const originalAddEventListener = window.addEventListener; | |
| window.addEventListener = function (type, listener, options) { | |
| if (type === "beforeunload") { | |
| showFlash("[POPUP BLOCKED] beforeunload handler blocked", "success"); | |
| return; | |
| } | |
| return originalAddEventListener.call(this, type, listener, options); | |
| }; | |
| document.addEventListener( | |
| "click", | |
| function (e) { | |
| const target = e.target.closest("a, button, [onclick]"); | |
| if (!target) return; | |
| const text = (target.textContent || "").toLowerCase(); | |
| const hrefAttr = (target.getAttribute("href") || "").toLowerCase(); | |
| const isAllowedDownloadElement = | |
| target.id === "downloadButton" || | |
| target.classList.contains("popsok") || | |
| target.matches(".download_link .input") || | |
| (text.includes("download") && | |
| (text.includes("mb") || | |
| hrefAttr.includes("/download/") || | |
| hrefAttr.includes("download"))); | |
| if (isAllowedDownloadElement) { | |
| showFlash("[ALLOWED] Download button click", "success"); | |
| return; | |
| } | |
| const suspiciousDomains = [ | |
| "googleads", | |
| "doubleclick", | |
| "google.com/aclk", | |
| "adclick", | |
| "popup", | |
| "/ad/", | |
| "/ads/", | |
| "ezoic", | |
| "ezodn", | |
| "googletagmanager", | |
| "googlesyndication", | |
| "taboola", | |
| "outbrain", | |
| ]; | |
| const isSuspicious = suspiciousDomains.some((domain) => | |
| hrefAttr.includes(domain), | |
| ); | |
| if (isSuspicious || target.hasAttribute("data-ad-link")) { | |
| showFlash( | |
| "[POPUP BLOCKED] Ad click intercepted: " + | |
| hrefAttr.substring(0, 150), | |
| "success", | |
| ); | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| e.stopImmediatePropagation(); | |
| return false; | |
| } | |
| }, | |
| true, | |
| ); | |
| const blockedScripts = [ | |
| "ezoic", | |
| "ezodn", | |
| "ezojs", | |
| "doubleclick", | |
| "googleads", | |
| "googletagmanager", | |
| "googletagservices", | |
| "quantserve", | |
| "google-analytics", | |
| "google.com/aclk", | |
| "facebook.com/plugins", | |
| "google.com/pagead", | |
| "googlesyndication", | |
| "taboola", | |
| "outbrain", | |
| ]; | |
| const originalAppendChild = Element.prototype.appendChild; | |
| const originalInsertBefore = Element.prototype.insertBefore; | |
| function shouldBlockScript(element) { | |
| if (!element || element.tagName !== "SCRIPT") return false; | |
| const src = (element.src || "").toLowerCase(); | |
| return blockedScripts.some((pattern) => src.includes(pattern)); | |
| } | |
| Element.prototype.appendChild = function (element) { | |
| if (shouldBlockScript(element)) { | |
| showFlash("[SCRIPT BLOCKED] appendChild: " + element.src, "success"); | |
| return element; | |
| } | |
| return originalAppendChild.call(this, element); | |
| }; | |
| Element.prototype.insertBefore = function (newElement, referenceElement) { | |
| if (shouldBlockScript(newElement)) { | |
| showFlash( | |
| "[SCRIPT BLOCKED] insertBefore: " + newElement.src, | |
| "success", | |
| ); | |
| return newElement; | |
| } | |
| return originalInsertBefore.call(this, newElement, referenceElement); | |
| }; | |
| const originalDocWrite = document.write; | |
| const originalDocWriteln = document.writeln; | |
| document.write = function (...args) { | |
| const content = args.join("").toLowerCase(); | |
| if (blockedScripts.some((pattern) => content.includes(pattern))) { | |
| showFlash("[SCRIPT BLOCKED] document.write", "success"); | |
| return; | |
| } | |
| return originalDocWrite.apply(document, args); | |
| }; | |
| document.writeln = function (...args) { | |
| const content = args.join("").toLowerCase(); | |
| if (blockedScripts.some((pattern) => content.includes(pattern))) { | |
| showFlash("[SCRIPT BLOCKED] document.writeln", "success"); | |
| return; | |
| } | |
| return originalDocWriteln.apply(document, args); | |
| }; | |
| /** | |
| * Searches for a download button element using multiple selector strategies. | |
| * @returns {Element|null} The download button element if found, otherwise null. | |
| */ | |
| function getDirectDownloadButton() { | |
| const selectors = [ | |
| ".download_link .input", | |
| "#downloadButton", | |
| "a.input.popsok", | |
| 'a[href*="download"][id="downloadButton"]', | |
| ".download-btn", | |
| '[data-testid="download-button"]', | |
| ]; | |
| for (const selector of selectors) { | |
| let button = document.querySelector(selector); | |
| if (!button && selector === "a.input.popsok") { | |
| button = Array.from(document.querySelectorAll("a")).find((el) => { | |
| const txt = (el.textContent || "").toLowerCase(); | |
| return ( | |
| el.classList.contains("popsok") || | |
| (txt.includes("download") && txt.includes("mb")) | |
| ); | |
| }); | |
| } | |
| if (button && button.href) { | |
| return button; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Attempts to find and click the download button, either by direct redirect or click event. | |
| * @returns {boolean} true if the button was found and a click/redirect was attempted, false otherwise | |
| */ | |
| async function clickDownloadButton() { | |
| const button = getDirectDownloadButton(); | |
| if (!button) return false; | |
| showFlash("[MF] Found download button", "success"); | |
| showFlash("[MF] Text: " + (button.textContent || "").trim(), "success"); | |
| showFlash("[MF] Href: " + button.href, "success"); | |
| try { | |
| if (button.href && /^https?:\/\//i.test(button.href)) { | |
| showFlash("[MF] Redirecting directly to download URL", "success"); | |
| location.replace(button.href); | |
| await sleep(8000); | |
| try { | |
| window.close(); | |
| } catch (e) {} | |
| return true; | |
| } | |
| } catch (err) { | |
| showFlash( | |
| "[MF] Direct redirect failed, fallback to click:" + err.message, | |
| "error", | |
| ); | |
| } | |
| try { | |
| const clickEvent = new MouseEvent("click", { | |
| bubbles: true, | |
| cancelable: true, | |
| view: window, | |
| button: 0, | |
| }); | |
| button.dispatchEvent(clickEvent); | |
| await sleep(300); | |
| try { | |
| button.click(); | |
| showFlash("[MF] Download button clicked", "success"); | |
| } catch (e) { | |
| showFlash("[MF] Native click failed: " + e.message, "error"); | |
| } | |
| return true; | |
| } catch (err) { | |
| showFlash("[MF] Failed to trigger button: " + err.message, "error"); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Waits for the download button to become available and clickable. | |
| * Attempts to click the button repeatedly at 300ms intervals until successful or max attempts reached. | |
| * @param {number} [maxAttempts=50] - Maximum number of attempts to find and click the download button | |
| * @returns {void} | |
| */ | |
| function waitForDownloadButton(maxAttempts = 50) { | |
| let attempts = 0; | |
| const interval = setInterval(() => { | |
| attempts++; | |
| if (clickDownloadButton()) { | |
| clearInterval(interval); | |
| return; | |
| } | |
| if (attempts >= maxAttempts) { | |
| clearInterval(interval); | |
| showFlash( | |
| "[MF] Download button not found after " + maxAttempts + " attempts", | |
| "error", | |
| ); | |
| } | |
| }, 300); | |
| } | |
| if (location.href.endsWith("#bulk")) { | |
| if (document.readyState === "loading") { | |
| document.addEventListener("DOMContentLoaded", () => | |
| waitForDownloadButton(), | |
| ); | |
| } else { | |
| waitForDownloadButton(); | |
| } | |
| originalAddEventListener.call(window, "load", async () => { | |
| await sleep(500); | |
| clickDownloadButton(); | |
| await sleep(1500); | |
| clickDownloadButton(); | |
| }); | |
| } else { | |
| if (document.readyState === "complete") { | |
| showFlash( | |
| "[MF] Auto click only available on bulk download pages", | |
| "success", | |
| ); | |
| return; | |
| } | |
| } | |
| } | |
| /** | |
| * folder page handle | |
| */ | |
| if (isFolderPage) { | |
| showFlash("[MF] Folder page detected.", "success"); | |
| /** | |
| * Determines the delay time before opening the next batch of download tabs based on the number of remaining downloads. | |
| * @param {number} remaining - The number of remaining download links that have not yet been opened. | |
| * @returns {number} The delay time in milliseconds before opening the next batch of tabs. | |
| */ | |
| function getBatchDelay(remaining) { | |
| if (remaining <= 0) return 0; | |
| if (remaining <= 2) return 3000; | |
| if (remaining <= 5) return 8000; | |
| if (remaining <= 15) return 15000; | |
| return 25000; | |
| } | |
| /** | |
| * Opens download links from DOM elements matching the selector. | |
| * @param {string} selector - CSS selector for list items containing download links. | |
| */ | |
| async function startDownload(selector) { | |
| const listItems = [...document.querySelectorAll(selector)]; | |
| console.log("[MF] Items found:", listItems.length, "selector:", selector); | |
| if (!listItems.length) { | |
| if (selector.includes("file_selected")) { | |
| showFlash("Select at least 1 file to download.", "error"); | |
| } else { | |
| showFlash("Folder list not found yet.", "error"); | |
| } | |
| return; | |
| } | |
| const urls = listItems | |
| .map((item) => item.querySelector("a")?.href) | |
| .filter(Boolean); | |
| if (!urls.length) { | |
| if (selector.includes("file_selected")) { | |
| showFlash("No download link found from selected items.", "error"); | |
| } else { | |
| showFlash("No downloadable links found.", "error"); | |
| } | |
| return; | |
| } | |
| const batchSize = 5; | |
| const delayPerTab = 1000; | |
| let opened = 0; | |
| for (let i = 0; i < urls.length; i += batchSize) { | |
| const batch = urls.slice(i, i + batchSize); | |
| for (const url of batch) { | |
| window.open(url + "#bulk", "_blank"); | |
| opened++; | |
| await sleep(delayPerTab); | |
| } | |
| const remaining = urls.length - (i + batch.length); | |
| const batchDelay = getBatchDelay(remaining); | |
| if (batchDelay > 0) { | |
| showFlash( | |
| `[MF] Waiting ${batchDelay / 1000}s before opening ${remaining} more tab(s)...`, | |
| "success", | |
| ); | |
| await sleep(batchDelay); | |
| } | |
| } | |
| console.log("[MF] Opened tabs:", opened); | |
| showFlash(`[MF] Opened ${opened} tabs total`, "success"); | |
| } | |
| /** | |
| * Injects a dropdown button with download options into the page. | |
| * Replaces the upgrade button frame with a wrapper containing a "DOWNLOAD ▼" button | |
| * that opens a menu with options to download all files or selected files. | |
| * @returns {void} | |
| */ | |
| function injectButton() { | |
| if (document.getElementById("mf-download-dropdown")) return; | |
| const upgradeButtonFrame = document.querySelector( | |
| ".upgrade_button_frame", | |
| ); | |
| if (!upgradeButtonFrame || !upgradeButtonFrame.parentNode) return; | |
| const wrapper = document.createElement("div"); | |
| wrapper.id = "mf-download-dropdown"; | |
| wrapper.style.position = "relative"; | |
| wrapper.style.display = "inline-block"; | |
| wrapper.style.marginRight = "10px"; | |
| const button = document.createElement("button"); | |
| button.type = "button"; | |
| button.textContent = "DOWNLOAD ▼"; | |
| button.classList.add("Btn", "Btn--greenUpgrade"); | |
| button.style.backgroundColor = "#33CC66"; | |
| button.style.color = "#222835"; | |
| button.style.border = "none"; | |
| button.style.cursor = "pointer"; | |
| const menu = document.createElement("div"); | |
| menu.style.position = "absolute"; | |
| menu.style.top = "100%"; | |
| menu.style.left = "0"; | |
| menu.style.display = "none"; | |
| menu.style.minWidth = "180px"; | |
| menu.style.background = "#fff"; | |
| menu.style.border = "1px solid #ccc"; | |
| menu.style.boxShadow = "0 2px 8px rgba(0,0,0,0.15)"; | |
| menu.style.zIndex = "9999"; | |
| /** | |
| * Creates a styled menu item element with hover and click behavior. | |
| * @param {string} label - The text to display in the menu item. | |
| * @param {Function} onClick - The callback function to execute when the item is clicked. | |
| * @returns {HTMLDivElement} A styled div element configured as a menu item. | |
| */ | |
| function createMenuItem(label, onClick) { | |
| const item = document.createElement("div"); | |
| item.textContent = label; | |
| item.style.padding = "10px 12px"; | |
| item.style.cursor = "pointer"; | |
| item.style.whiteSpace = "nowrap"; | |
| item.addEventListener("mouseenter", () => { | |
| item.style.background = "#f2f2f2"; | |
| }); | |
| item.addEventListener("mouseleave", () => { | |
| item.style.background = "#fff"; | |
| }); | |
| item.addEventListener("click", (e) => { | |
| e.stopPropagation(); | |
| menu.style.display = "none"; | |
| onClick(); | |
| }); | |
| return item; | |
| } | |
| const downloadAllItem = createMenuItem("Download All", () => { | |
| startDownload("#main_list > li"); | |
| }); | |
| const downloadSelectedItem = createMenuItem("Download Selected", () => { | |
| const selectedSelector = '#main_list > li[id^="file"].file_selected'; | |
| if (!document.querySelector(selectedSelector)) { | |
| showFlash("Select at least 1 file to download.", "error"); | |
| return; | |
| } | |
| startDownload(selectedSelector); | |
| }); | |
| button.addEventListener("click", (e) => { | |
| e.stopPropagation(); | |
| menu.style.display = menu.style.display === "block" ? "none" : "block"; | |
| }); | |
| document.addEventListener("click", () => { | |
| menu.style.display = "none"; | |
| }); | |
| menu.appendChild(downloadAllItem); | |
| menu.appendChild(downloadSelectedItem); | |
| wrapper.appendChild(button); | |
| wrapper.appendChild(menu); | |
| upgradeButtonFrame.parentNode.replaceChild(wrapper, upgradeButtonFrame); | |
| showFlash("[MF] Download dropdown injected.", "success"); | |
| } | |
| /** | |
| * Waits for the folder UI to be ready by repeatedly attempting to inject the button. | |
| * @param {number} [maxAttempts=60] - The maximum number of attempts to inject the button before stopping. | |
| * @returns {void} | |
| */ | |
| function waitForFolderUI(maxAttempts = 60) { | |
| let attempts = 0; | |
| const interval = setInterval(() => { | |
| attempts++; | |
| injectButton(); | |
| if ( | |
| document.getElementById("mf-download-all-btn") || | |
| attempts >= maxAttempts | |
| ) { | |
| clearInterval(interval); | |
| } | |
| }, 500); | |
| } | |
| if (document.readyState === "loading") { | |
| document.addEventListener("DOMContentLoaded", waitForFolderUI); | |
| } else { | |
| waitForFolderUI(); | |
| } | |
| window.addEventListener("load", async () => { | |
| await sleep(500); | |
| injectButton(); | |
| await sleep(1500); | |
| injectButton(); | |
| }); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment