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

2025-10-05 기준 버전입니다. 최신 버전을 확인하세요.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Pixeldrain DL Bypass
// @namespace    https://greasyfork.org/users/821661
// @version      1.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", ""),

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

function createElementWithListener(html, type, listener) {
    const div = document.createElement("div");
    div.style.setProperty("display", "contents", "important");
    div.innerHTML = html;
    div.addEventListener(type, listener);
    return div;
}

function createButtonHTML(iconName, title, text, extraClass = "") {
    return `
        <button class="toolbar_button svelte-jngqwx ${extraClass}" title="${title}">
            <i class="icon">${iconName}</i>
            <span class="svelte-jngqwx">${text}</span>
        </button>
    `;
}

function showModal(title, content) {
    const MODAL_HTML = `
        <div class="background svelte-1f8gt9n" style="z-index: 10001;" role="dialog">
            <div class="window svelte-1f8gt9n" role="dialog" aria-modal="true">
                <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;">
                        ${content}
                    </div>
                </div>
            </div>
        </div>
    `;

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

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

function downloadFile(fileName, fileID, el) {
    return new Promise((resolve, reject) => {
        const selectedDomain = PDLB_CONFIG.customBypassURL || PDLB_CONFIG.defaultBypassURL;
        const url = `https://${selectedDomain}/${fileID}`;

        GM_xmlhttpRequest({
            url,
            responseType: "blob",
            onload(event) {
                resolve(event);
                if (event.status !== 200) {
                    showModal("Download error", "The server probably blocked this download.");
                    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 {
            await downloadFile(file.name, file.id, el);
        } catch (error) {
            console.error(`Failed to download ${file.name}:`, error);
            showModal("Download error", `Failed to download ${file.name}.`);
        }
    }
}

function copyBypassURL(fileID) {
    const selectedDomain = PDLB_CONFIG.customBypassURL || PDLB_CONFIG.defaultBypassURL;
    const url = `https://${selectedDomain}/${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."));
}

function handleSingleFile(separator, fileData) {
    const { name, id } = fileData;
    const downloadButtonHTML = createButtonHTML("download", "Bypass download", "Download bypass", "download-bypass");
    const copyButtonHTML = createButtonHTML("content_copy", "Copy bypass url", "Copy bypass URL");

    const downloadButton = createElementWithListener(downloadButtonHTML, "click", () => downloadFile(name, id, downloadButton.firstElementChild));
    const copyButton = createElementWithListener(copyButtonHTML, "click", () => copyBypassURL(id));

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

function handleFileList(separator, listData) {
    const selectedDomain = PDLB_CONFIG.customBypassURL || PDLB_CONFIG.defaultBypassURL;

    const availableFiles = listData.files.filter((file) => file.availability === "");
    const fileUrls = availableFiles.map((file) => `<a href="https://${selectedDomain}/${file.id}">${file.name}</a>`).join("<br>");
    const containerFileUrls = `<div class="indent" style="display: flex; flex-direction: column;justify-content: center;align-items: center;">${fileUrls}</div>`;
    const bypassUrls = availableFiles.map((file) => `https://${selectedDomain}/${file.id}`).join("\n");
    const containerBypassUrls = `<pre class="indent" style="padding-inline: .5rem;">${bypassUrls}</pre>`;

    const dlSelectedButtonHTML = createButtonHTML("download", "Bypass download selected file", "Download selected file");
    const dlAllButtonHTML = createButtonHTML("download", "Bypass download all files", "Download all files");
    const copyButtonHTML = createButtonHTML("content_copy", "Copy bypass url", "Copy bypass URL");
    const showUrlsButtonHTML = createButtonHTML("link", "Show bypass URLs", "Show bypass URLs");

    const dlAllButton = createElementWithListener(dlAllButtonHTML, "click", () => massiveDownload(availableFiles, dlAllButton.firstElementChild));
    const showUrlsButton = createElementWithListener(showUrlsButtonHTML, "click", () => showModal("Bypass URLs", containerFileUrls + containerBypassUrls));
    const copyButton = createElementWithListener(copyButtonHTML, "click", () => {
        const selectedFile = listData.files.find((file) => file.selected);
        copyBypassURL(selectedFile.id);
    });
    const dlSelectedButton = createElementWithListener(dlSelectedButtonHTML, "click", () => {
        const selectedFile = listData.files.find((file) => file.selected);
        if (selectedFile.availability !== "") {
            showModal(selectedFile.availability, selectedFile.availability_message);
            return;
        }
        downloadFile(selectedFile.name, selectedFile.id, dlSelectedButton.firstElementChild);
    });

    separator.insertAdjacentElement("afterend", dlSelectedButton);
    dlSelectedButton.insertAdjacentElement("afterend", dlAllButton);
    dlAllButton.insertAdjacentElement("afterend", copyButton);
    copyButton.insertAdjacentElement("afterend", showUrlsButton);
}

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

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"]) { display: none !important; }`);

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

init();