IEEE Paper Downloader

IEEE Paper Downloader is a Greasemonkey script that enables batch downloading of papers. It bypasses the IEEE Xplore's limit on downloading up to 10 papers at a time, allowing you to download more than 10 papers in one go.

// ==UserScript==
// @name         IEEE Paper Downloader
// @namespace    https://ieeexplore.ieee.org/
// @version      1.2
// @description  IEEE Paper Downloader is a Greasemonkey script that enables batch downloading of papers. It bypasses the IEEE Xplore's limit on downloading up to 10 papers at a time, allowing you to download more than 10 papers in one go.
// @author       OccDeser
// @match        https://ieeexplore.ieee.org/*/proceeding*
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @license MIT
// ==/UserScript==

const DOWNLOAD_INTERVAL = 1000; // Download interval in milliseconds

const CSS = `
.paper-downloader {
    margin-left: 15px;
    margin-right: 15px;
    padding-top: 15px;
    padding-bottom: 15px;
    background-color: #f5f5f5;
}

.paper-downloader span {
    padding: 5px;
}

.ipd-modal-background {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5); /* Gray background with 50% transparency */
    display: flex;
    justify-content: center; /* Horizontal centering */
    align-items: center; /* Vertical centering */
    z-index: 999; /* Place modal on top */
    flex-direction: column;
}

.ipd-modal-body {
    width: 75%;
    padding: 20px;
    background: #fff;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.ipd-modal-content {
    height: 450px;
    overflow-y: auto;
}
`

const HTML = `
<div class="paper-downloader">
<span>
    <xpl-conference-toc-dashboard>
        <strong class="text-base-md-lh">
            IEEE Paper Downloader
        </strong>
    </xpl-conference-toc-dashboard>
</span>
<span>
    <xpl-conference-toc-dashboard>
        <strong class="text-base-md-lh" id="ipd-strong-countdown">
        </strong>
    </xpl-conference-toc-dashboard>
</span>
<span>
<xpl-export-search-results>
    <button class="xpl-toggle-btn" id="ipd-button-showall">
        <a class="text-white">
            Show All
        </a>
    </button>
</xpl-export-search-results>
</span>
<span>
<xpl-export-search-results>
    <button class="xpl-toggle-btn" id="ipd-button-select">
        <a class="text-white">
            Select
        </a>
    </button>
</xpl-export-search-results>
</span>
<span>
<xpl-export-search-results>
    <button class="xpl-toggle-btn" id="ipd-button-download">
        <a class="text-white">
            Download
        </a>
    </button>
</xpl-export-search-results>
</span>
</div>
`

var allPapers = {};
var selectedPapers = {};

const createModal = (content, onClose) => {
    const modalHtml = `
        <div class="ipd-modal-background">
            <div class="ipd-modal-body">
                <div class="ipd-modal-content">
                    ${content}
                </div>
                <xpl-export-search-results>
                    <button class="xpl-toggle-btn" id="ipd-modal-close">
                        <a class="text-white">Close</a>
                    </button>
                </xpl-export-search-results>
            </div>
        </div>
    `

    let modalElement = document.createElement('div');
    modalElement.innerHTML = modalHtml;
    document.body.appendChild(modalElement);

    // Bind listener
    let modalCloseButton = document.getElementById('ipd-modal-close');
    modalCloseButton.addEventListener('click', () => {
        modalElement.remove();
        onClose();
    });
}

const downloadPapers = () => {
    const downloadLinks = [];
    for (let id in selectedPapers) {
        if (selectedPapers[id]) {
            let url = new URL("https://ieeexplore.ieee.org" + allPapers[id].href);
            const arnumber = url.searchParams.get('arnumber');
            downloadLinks.push(`https://ieeexplore.ieee.org/stampPDF/getPDF.jsp?tp=&arnumber=${arnumber}&isBulkDownload=true`);
        }
    }
    console.log(downloadLinks);

    // Show download progress
    createModal(
        '<div id="ipd-modal-progress"></div>',
        () => {
            console.log('Modal closed');
        }
    );

    let progress = document.getElementById('ipd-modal-progress');
    let progressCounter = 0;

    const progressInfo = (message) => {
        let messageBox = document.createElement('div');
        messageBox.innerHTML = message;
        messageBox.style = "margin-bottom: 5px;";
        progress.insertBefore(messageBox, progress.firstChild);
    }

    const progressTop = (message) => {
        let topMessageBox = document.getElementById('ipd-modal-progress-top');
        if (topMessageBox) {
            topMessageBox.remove();
        }

        let messageBox = document.createElement('div');
        messageBox.id = 'ipd-modal-progress-top';
        messageBox.innerHTML = message;
        messageBox.style = "margin-bottom: 5px; font-weight: bold;";
        progress.insertBefore(messageBox, progress.firstChild);
    }
    progressInfo(`Downloading ${downloadLinks.length} papers...`);

    // Store downloaded content
    const downloadedContent = [];

    // Use GM_xmlhttpRequest to fetch link content
    const fetchContent = (url) => {
        let filename = new URL(url).searchParams.get('arnumber') + '.pdf';

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'arraybuffer',
                onload: response => resolve(response.response),
                onerror: error => reject(error),
                onprogress: progressEvent => {
                    let size = progressEvent.loaded / 1024 / 1024;
                    progressTop(`${filename} Downloading: ${size.toFixed(2)}MB`);
                }
            });
        });
    }

    // Iterate through download links and fetch content
    const fetchAndDownload = async () => {
        for (const link of downloadLinks) {
            try {
                let filename = new URL(link).searchParams.get('arnumber') + '.pdf';
                const content = await fetchContent(link);
                downloadedContent.push({ filename: filename, content });
                progressCounter++;
                progressInfo(`[${progressCounter}/${downloadLinks.length}] Fetched content from: ${link}`);
                progressTop(`${filename} Downloaded, continuing in ${DOWNLOAD_INTERVAL / 1000} seconds...`);
                await new Promise(resolve => setTimeout(resolve, DOWNLOAD_INTERVAL));
            } catch (error) {
                console.error(`Error fetching content from link: ${error}`);
            }
        }

        // Compress all content into a single zip file and download
        const zip = new JSZip();
        for (const item of downloadedContent) {
            zip.file(item.filename, item.content);
        }

        zip.generateAsync({ type: 'blob' })
            .then(blob => {
                GM_download({
                    url: URL.createObjectURL(blob),
                    name: 'papers.zip'
                });
            });
    }

    fetchAndDownload();
}

const selectPapers = () => {
    const xpathExpression = '//div[@class="subid-container"]//xpl-access-type-icon/span[not(contains(@class, "locked"))]/../../../..';
    let paperResults = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if (paperResults.snapshotLength > 0) {
        let paperElement = paperResults.snapshotItem(0);
        let paperHref = paperElement.getElementsByTagName('xpl-view-pdf')[0].getElementsByTagName('a')[0].getAttribute('href');
        let paperTitle = paperElement.getElementsByTagName('h2')[0].textContent;
        let id = 0 + '-' + paperTitle.slice(0, 16) + '-' + paperHref.slice(-8);
        let first_id = Object.keys(allPapers).filter((key) => key.startsWith('0'))[0];

        // Check if the page has been updated
        if (Object.keys(allPapers).length !== paperResults.snapshotLength || id !== first_id) {
            allPapers = {};
            selectedPapers = {};
            for (let i = 0; i < paperResults.snapshotLength; i++) {
                let paperElement = paperResults.snapshotItem(i);
                let paperHref = paperElement.getElementsByTagName('xpl-view-pdf')[0].getElementsByTagName('a')[0].getAttribute('href');
                let paperTitle = paperElement.getElementsByTagName('h2')[0].textContent;
                let id = i + '-' + paperTitle.slice(0, 16) + '-' + paperHref.slice(-8);
                allPapers[id] = {
                    'title': paperTitle,
                    'href': paperHref,
                    'element': paperElement
                };
                selectedPapers[id] = true;
            }
        }
    }

    // Create modal
    createModal(
        '<form id="ipd-select-dynamic-form"></form>',
        () => {
            let counter = 0;
            for (let id in selectedPapers) {
                if (selectedPapers[id]) {
                    counter++;
                }
            }
            console.log(`Selected ${counter} papers`);
        }
    )

    // Create select form
    let dynamicForm = document.getElementById('ipd-select-dynamic-form');
    let items = []
    for (let id in allPapers) {
        items.push({ id, title: allPapers[id].title });
    }
    items.forEach(function (item, _) {
        var checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.name = item.title;
        checkbox.value = item.id;
        checkbox.checked = selectedPapers[item.id];
        checkbox.addEventListener('change', function () {
            selectedPapers[checkbox.value] = checkbox.checked;
        });

        var label = document.createElement('label');
        label.appendChild(checkbox);

        var titleBox = document.createElement('span');
        titleBox.appendChild(document.createTextNode(item.title));
        titleBox.style = "margin-left: 5px;";
        label.appendChild(titleBox);

        var itemDiv = document.createElement('div');
        itemDiv.style = "margin-bottom: 5px;";
        itemDiv.appendChild(label);

        dynamicForm.appendChild(itemDiv);
    });
}

const showAll = () => {
    console.log("showAll");

    // Get the current rowsPerPage
    const url = new URL(window.location.href);
    const current_rows = eval(url.searchParams.get('rowsPerPage'))

    // Get the total number of rows
    const xpathExpression = "//span[contains(., 'Showing')]/span[2]/text()";
    const result = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if (result.snapshotLength == 0) {
        console.log("Cannot find total rows");
        return;
    }

    const total_rows = eval(result.snapshotItem(0).textContent);

    if (current_rows >= total_rows) {
        console.log("Already showing all");
        return;
    }

    url.searchParams.set('rowsPerPage', total_rows.toString());
    const modifiedURL = url.toString();
    window.location.href = modifiedURL;
}

const bindListener = () => {
    let buttonSelect = document.getElementById('ipd-button-select');
    let buttonDownload = document.getElementById('ipd-button-download');
    let buttonShowAll = document.getElementById('ipd-button-showall');

    buttonSelect.addEventListener('click', selectPapers);
    buttonDownload.addEventListener('click', downloadPapers);
    buttonShowAll.addEventListener('click', showAll);
}

(function () {
    'use strict';

    let mainElement = document.getElementById('xplMainContentLandmark');

    if (mainElement) {
        // Insert CSS codes
        let style = document.createElement('style');
        style.appendChild(document.createTextNode(CSS));
        document.head.appendChild(style);

        // Insert HTML codes
        let optionHeader = document.createElement('div');
        optionHeader.innerHTML = HTML;
        let firstChild = mainElement.firstChild;
        mainElement.insertBefore(optionHeader, firstChild);

        // Add event listener
        bindListener();

        // Set the countdown
        let timerCounter = 0;
        let countDownTimer = setInterval(() => {
            let countDownElement = document.getElementById('ipd-strong-countdown');
            timerCounter = timerCounter + 100;
            countDownElement.innerHTML = "Loading... " + timerCounter / 1000 + "s";
        }, 100);

        // Wait for loading
        const loadPapers = () => {
            const xpathExpression = '//div[@class="subid-container"]//xpl-access-type-icon/span[not(contains(@class, "locked"))]/../../../..';
            let paperResults = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
            if (paperResults.snapshotLength > 0) {
                clearInterval(countDownTimer);
                let countDownElement = document.getElementById('ipd-strong-countdown');
                countDownElement.innerHTML = "Loading complete!";
            } else {
                setTimeout(loadPapers, 100);
                return;
            }
        }
        setTimeout(loadPapers, 100);
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址