Pixeldrain DL Bypass

Adds direct-download buttons and links for Pixeldrain files using an alternate proxy — inspired by 'Pixeldrain Download Bypass' by hhoneeyy and MegaLime0

Per 12-10-2025. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name            Pixeldrain DL Bypass
// @namespace       https://greasyfork.org/users/821661
// @version         1.5.2
// @description     Adds direct-download buttons and links for Pixeldrain files using an alternate proxy — inspired by 'Pixeldrain Download Bypass' by hhoneeyy and MegaLime0
// @author          hdyzen
// @match           https://pixeldrain.com/*
// @match           https://pixeldrain.net/*
// @icon            https://www.google.com/s2/favicons?domain=pixeldrain.com/&sz=64
// @grant           GM_xmlhttpRequest
// @grant           GM_registerMenuCommand
// @grant           GM_addStyle
// @grant           GM_getValue
// @grant           GM_setValue
// @license         GPL-3.0-only
// ==/UserScript==

const PDLB_CONFIG = {
    defaultBypassURL: "pd.1drv.eu.org",
    customBypassURL: GM_getValue("custom_proxy", ""),
    jDownloaderURL: "http://127.0.0.1:9666/flash/addcnl",

    preferences: {
        scrollbarNames: {
            state: GM_getValue("scrollbar-names", false),
            effect: () => GM_addStyle(".container-names { overflow: scroll; } .file-name { overflow: initial }"),
        },
        directDownload: {
            state: GM_getValue("direct-download", false),
        },
    },

    viewer_data: unsafeWindow.viewer_data,
    api_response: unsafeWindow.viewer_data.api_response,
    dataType: unsafeWindow.viewer_data.type,
};

const selectedProxy = PDLB_CONFIG.customBypassURL || PDLB_CONFIG.defaultBypassURL;

function createButton(iconName, title, text) {
    const template = document.createElement("template");
    template.innerHTML = `
        <button class="toolbar_button svelte-jngqwx" title="${title}">
            <i class="icon">${iconName}</i>
            <span class="svelte-jngqwx">${text}</span>
        </button>
    `;

    return template.content.firstElementChild;
}

function showModal(title, content, extraContent = "") {
    const MODAL_HTML = `
        <div class="background svelte-1f8gt9n" style="z-index: 10001;" role="dialog">
            <div class="window svelte-1f8gt9n" role="dialog" aria-modal="true" style="max-height: 80%; max-width: 80%; ">
                <div class="header svelte-1f8gt9n">
                    <span class="title svelte-1f8gt9n" style="padding-inline: calc(2rem + 32px) 2rem;">${title}</span>
                    <button class="button svelte-1ef47mx round close-button">
                        <i class="icon">close</i>
                    </button>
                </div>
                <div class="body svelte-1f8gt9n" style="padding: 1rem;">
                    <div class="container svelte-1j8hfe6" style="display: flex; flex-direction: row;justify-content: center;align-items: center;">
                        ${content}
                    </div>
                    <div class="container svelte-1j8hfe6" style="display: flex; flex-direction: row;justify-content: center;align-items: center;">
                        ${extraContent}
                    </div>
                </div>
            </div>
        </div>
    `;

    const template = document.createElement("template");
    template.innerHTML = MODAL_HTML;

    const modalElement = template.content.firstElementChild;

    modalElement.addEventListener("click", (event) => {
        if (event.target.matches(".background") || event.target.closest(".close-button")) {
            modalElement.remove();
        }
    });

    document.body.insertAdjacentElement("afterbegin", modalElement);

    return () => modalElement.remove();
}

function downloadFile(fileName, fileID, el) {
    return new Promise((resolve, reject) => {
        const url = `https://${selectedProxy}/${fileID}`;

        if (PDLB_CONFIG.preferences.directDownload.state) {
            window.open(url, "_blank");
            return resolve();
        }

        GM_xmlhttpRequest({
            url,
            responseType: "blob",
            onload(event) {
                resolve(event);
                if (event.status !== 200) {
                    showModal("Download error", `The server probably blocked download of the file.`);
                    return;
                }

                const a = document.createElement("a");
                a.target = "_blank";
                a.href = URL.createObjectURL(event.response);
                a.download = fileName;
                a.click();
                el.style.setProperty("--loaded", "0");
            },
            onerror(event) {
                reject(event);
            },
            onprogress(event) {
                el.style.setProperty("--loaded", `${(event.loaded * el.clientWidth) / event.total}px`);
            },
        });
    });
}

async function massiveDownload(files, el) {
    for (const file of files) {
        try {
            if (file.availability || file.availability_message) {
                showModal(
                    file.availability,
                    `Error on download file: ${file.name}<br>Server availability message: ${file.availability_message}.<br>Trying download of others files.`,
                );
                continue;
            }

            const res = await downloadFile(file.name, file.id, el);

            if (res.loaded === 0) {
                throw new Error("0 bytes loaded from response.");
            }
        } catch (error) {
            console.error(`Failed to download ${file.name}:`, error);
            showModal("Download error", `Failed to download ${file.name}.`);
        }
    }
}

function copyBypassURL(fileID) {
    const url = `https://${selectedProxy}/${fileID}`;
    navigator.clipboard
        .writeText(url)
        .then(() => showModal("URL copied", "The bypass URL has been copied to your clipboard."))
        .catch(() => showModal("Copy failed", "Could not copy the URL. Please copy it manually."));
}

async function openBypassURL(file) {
    const w = window.open("about:blank", "_blank");
    const finalURL = file.proxyFinalURL || (await getFinalURL(`https://${selectedProxy}/${file.id}`));
    w.document.write(`
        <html style="background: #000;">
            <body style="margin:0">
            <video src="${finalURL}" controls autoplay style="width:100%;height:100%"></video>
            </body>
        </html>
    `);
    w.document.close();
}

function handleSingleFile(separator, fileData) {
    const { name, id } = fileData;
    const downloadButton = createButton("download", "Bypass download", "Download bypass");
    const openButton = createButton("open_in_new", "Open bypass URL", "Open bypass");
    const copyButton = createButton("content_copy", "Copy bypass URL", "Copy bypass");
    const sendToJDownloaderButton = createButton("add_link", "Add link to JDownloader", "Add link to JDownloader");

    downloadButton.addEventListener("click", () => downloadFile(name, id, downloadButton));
    openButton.addEventListener("click", async () => {
        openBypassURL(fileData);
    });
    copyButton.addEventListener("click", () => copyBypassURL(id));
    sendToJDownloaderButton.addEventListener("click", async () => {
        sendToJDownloader(fileData.proxyFinalURL);
    });

    setBypassURL([fileData]).then(() => document.body.classList.add("final-urls-loaded"));

    separator.insertAdjacentElement("afterend", downloadButton);
    downloadButton.insertAdjacentElement("afterend", openButton);
    openButton.insertAdjacentElement("afterend", copyButton);
    copyButton.insertAdjacentElement("afterend", sendToJDownloaderButton);
    sendToJDownloaderButton.insertAdjacentElement("afterend", separator.cloneNode());
}

function handleFileList(separator, listData) {
    const availableFiles = [];
    const bypassURLS = [];
    let filesLinkHTML = "";

    for (const file of listData.files) {
        if (file.availability || file.availability_message) continue;

        availableFiles.push(file);

        bypassURLS.push(`https://${selectedProxy}/${file.id}`);

        filesLinkHTML += `
            <a class="file-name" 
                target="_blank" 
                rel="noopener noreferrer" 
                title="${file.name}" 
                href="https://${selectedProxy}/${file.id}">
                    ${file.name}
            </a>`;
    }

    const containerFileURLS = `<div class="indent container-names" style="display: flex; flex-direction: column;justify-content: center;align-items: center;">${filesLinkHTML}</div>`;
    const containerBypassURLS = `<pre class="indent" style="padding-inline: .5rem; overflow: initial;">${bypassURLS.join("\n")}</pre>`;

    const dlSelectedButton = createButton("download", "Bypass download selected file", "Download selected");
    const dlAllButton = createButton("download", "Bypass download all files", "Download all");
    const openButton = createButton("open_in_new", "Open bypass URL", "Open bypass");
    const copyButton = createButton("content_copy", "Copy bypass url", "Copy bypass");
    const showUrlsButton = createButton("link", "Show bypass URLs", "Show bypass");
    const sendAllToJDButton = createButton("add_link", "Add links to JDownloader", "Add links to JDownloader");
    const sendSelectedToJDButton = createButton("add_link", "Add link to JDownloader", "Add link to JDownloader");

    dlSelectedButton.addEventListener("click", () => {
        const selectedFile = listData.files.find((file) => file.selected);
        if (selectedFile.availability || selectedFile.availability_message) {
            showModal(selectedFile.availability, selectedFile.availability_message);
            return;
        }

        downloadFile(selectedFile.name, selectedFile.id, dlSelectedButton);
    });
    dlAllButton.addEventListener("click", () => massiveDownload(listData.files, dlAllButton));
    openButton.addEventListener("click", async () => {
        const selectedFile = listData.files.find((file) => file.selected);

        openBypassURL(selectedFile);
    });
    copyButton.addEventListener("click", () => {
        const selectedFile = listData.files.find((file) => file.selected);

        copyBypassURL(selectedFile.id);
    });
    showUrlsButton.addEventListener("click", () => showModal("Bypass URLs", containerFileURLS + containerBypassURLS));
    sendAllToJDButton.addEventListener("click", async () => {
        const urls = listData.files.map((file) => file.proxyFinalURL).join("\r\n");

        sendToJDownloader(urls);
    });
    sendSelectedToJDButton.addEventListener("click", async () => {
        const selectedFile = listData.files.find((file) => file.selected);

        sendToJDownloader(selectedFile.proxyFinalURL);
    });

    setBypassURL(availableFiles).then(() => document.body.classList.add("final-urls-loaded"));

    separator.insertAdjacentElement("afterend", dlSelectedButton);
    dlSelectedButton.insertAdjacentElement("afterend", dlAllButton);
    dlAllButton.insertAdjacentElement("afterend", openButton);
    openButton.insertAdjacentElement("afterend", copyButton);
    copyButton.insertAdjacentElement("afterend", showUrlsButton);
    showUrlsButton.insertAdjacentElement("afterend", sendSelectedToJDButton);
    sendSelectedToJDButton.insertAdjacentElement("afterend", sendAllToJDButton);
    sendAllToJDButton.insertAdjacentElement("afterend", separator.cloneNode());
}

function registerCommands() {
    GM_registerMenuCommand(`Current proxy: ${PDLB_CONFIG.customBypassURL || PDLB_CONFIG.defaultBypassURL}`, () => {});
    GM_registerMenuCommand("Set custom proxy", () => {
        const proxyDomain = prompt("Set your custom proxy", GM_getValue("custom_proxy", ""));

        GM_setValue("custom_proxy", proxyDomain || "");

        unsafeWindow.location.reload();
    });

    GM_registerMenuCommand(`${PDLB_CONFIG.preferences.directDownload.state ? "ON" : "OFF"}: Direct download`, () => {
        GM_setValue("direct-download", !PDLB_CONFIG.preferences.directDownload.state);

        unsafeWindow.location.reload();
    });

    GM_registerMenuCommand(`${PDLB_CONFIG.preferences.scrollbarNames.state ? "ON" : "OFF"}: Horizontal scrollbar for long names`, () => {
        GM_setValue("scrollbar-names", !PDLB_CONFIG.preferences.scrollbarNames.state);

        unsafeWindow.location.reload();
    });
}

async function setBypassURL(availableFiles) {
    const promises = availableFiles.map(async (file) => {
        const finalURL = await getFinalURL(`https://${selectedProxy}/${file.id}`);
        file.proxyFinalURL = finalURL.replace("?download", "");
    });

    await Promise.all(promises);
}

function getFinalURL(url) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            url,
            method: "HEAD",
            onload(event) {
                resolve(event.finalUrl);
            },
            onerror: (ev) => {
                console.error("Error getting final URL", ev);
                showModal("Error getting final URL", "The proxy is probably down or blocking the request.");
                reject(ev);
            },
        });
    });
}

function sendToJDownloader(urls) {
    const data = {
        urls,
        source: urls,
    };

    GM_xmlhttpRequest({
        method: "POST",
        url: PDLB_CONFIG.jDownloaderURL,
        headers: {
            "Content-Type": "application/json",
        },
        data: JSON.stringify(data),
        onerror(ev) {
            console.error("Error sending to jDownloader", ev);
            showModal("Error sending to jDownloader", "jDownloader is probably closed or listening on another port.");
        },
    });
}

function init() {
    if (!PDLB_CONFIG.viewer_data) {
        console.warn("Viewer data not found. Script may not function correctly.");
        return;
    }

    const separator = document.querySelector(".toolbar > .separator.svelte-jngqwx");
    if (!separator) {
        console.warn("Toolbar separator not found. Cannot add buttons.");
        return;
    }

    GM_addStyle(`
        .file_preview_row:has(.gallery) :where([title="Bypass download selected file"], [title="Copy bypass url"], [title="Add link to JDownloader"], [title="Open bypass URL"]) { 
            display: none !important; 
        }
        [title="Bypass download"], [title="Bypass download selected file"], [title="Bypass download all files"] { 
            box-shadow: inset var(--highlight_background) var(--loaded, 0) 0; 
            transition: 0.3s;
        }
        [title="Add link to JDownloader"], [title="Add links to JDownloader"] {
            display: none;
        }
        .final-urls-loaded :where([title="Add link to JDownloader"], [title="Add links to JDownloader"]) {
            display: block;
        }
        .file-name {
            max-width: 550px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }
    `);

    switch (PDLB_CONFIG.dataType) {
        case "file":
            handleSingleFile(separator, PDLB_CONFIG.api_response);
            break;
        case "list":
            handleFileList(separator, PDLB_CONFIG.api_response);
            break;
        default:
            console.warn(`File type "${PDLB_CONFIG.dataType}" not supported.`);
    }

    registerCommands();

    for (const key in PDLB_CONFIG.preferences) {
        const state = PDLB_CONFIG.preferences[key].state;

        if (state) PDLB_CONFIG.preferences[key].effect?.();
    }
}

init();