Greasy Fork

Google Scholar to Sci-Hub

Adds Sci-Hub, LibGen, and LibSTC buttons to Google Scholar results for papers after 2020

目前为 2025-02-03 提交的版本。查看 最新版本

// ==UserScript==
// @name         Google Scholar to Sci-Hub
// @namespace    ScholarToSciHub
// @version      1.2
// @description  Adds Sci-Hub, LibGen, and LibSTC buttons to Google Scholar results for papers after 2020
// @author       Bui Quoc Dung (modified)
// @include      https://scholar.google.*/*
// @license      AGPL-3.0-or-later
// @grant        GM_xmlhttpRequest
// ==/UserScript==

const SCIHUB_URL = 'https://www.tesble.com/';
const LIBGEN_URL = 'https://libgen.li/index.php?req=';
const LIBSTC_BASE_URL = 'https://hub.libstc.cc/';

// Regular expression for matching DOI
const DOI_REGEX = /\b(10\.\d{4,}(?:\.\d+)*\/(?:(?!["&'<>])\S)+)\b/gi;

function extractYear(element) {
    const yearMatch = element.textContent.match(/\d{4}/);
    return yearMatch ? parseInt(yearMatch[0]) : 0;
}

function checkPDFExistence(url, callback) {
    GM_xmlhttpRequest({
        method: 'HEAD',
        url: url,
        onload: function(response) {
            const isPDF = response.status === 200 &&
                         response.responseHeaders.toLowerCase().includes('application/pdf');
            callback(isPDF);
        },
        onerror: function() {
            callback(false);
        }
    });
}

function checkLibGenPDF(title, buttonContainer) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: LIBGEN_URL + encodeURIComponent(title),
        onload: function(response) {
            // Check if response contains any mention of "pdf" (case insensitive)
            const hasResults = response.responseText.toLowerCase().includes('pdf');

            const libGenLink = document.createElement('a');
            libGenLink.textContent = hasResults ? '[PDF] LibGen' : '[NoPDF] LibGen';
            libGenLink.href = LIBGEN_URL + encodeURIComponent(title);
            libGenLink.target = '_blank';
            buttonContainer.appendChild(libGenLink);
        },
        onerror: function(error) {
            console.error('Error checking LibGen:', error);
            const libGenLink = document.createElement('a');
            libGenLink.textContent = '[NoPDF] LibGen';
            libGenLink.href = LIBGEN_URL + encodeURIComponent(title);
            libGenLink.target = '_blank';
            buttonContainer.appendChild(libGenLink);
        }
    });
}

function checkSciHubPDF(url, buttonContainer, callback) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: SCIHUB_URL + url,
        onload: function(response) {
            // Check if response contains PDF embed or download link
            const hasPDF = response.responseText.includes('iframe') ||
                          response.responseText.includes('pdf') ||
                          response.responseText.includes('embed');

            const sciHubLink = document.createElement('a');
            sciHubLink.textContent = hasPDF ? '[PDF] Sci-Hub' : '[NoPDF] Sci-Hub';
            sciHubLink.href = SCIHUB_URL + url;
            sciHubLink.target = '_blank';
            buttonContainer.appendChild(sciHubLink);

            if (callback) callback(hasPDF);
        },
        onerror: function(error) {
            console.error('Error checking Sci-Hub:', error);
            const sciHubLink = document.createElement('a');
            sciHubLink.textContent = '[NoPDF] Sci-Hub';
            sciHubLink.href = SCIHUB_URL + url;
            sciHubLink.target = '_blank';
            buttonContainer.appendChild(sciHubLink);

            if (callback) callback(false);
        }
    });
}

function fetchDOIAndAddButton(titleLink, buttonContainer) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: titleLink.href,
        onload: function(response) {
            const matches = response.responseText.match(DOI_REGEX);
            if (matches && matches.length > 0) {
                const firstDOI = matches[0];
                const pdfURL = LIBSTC_BASE_URL + firstDOI + '.pdf';

                const libstcLink = document.createElement('a');
                libstcLink.href = pdfURL;
                libstcLink.target = '_blank';

                checkPDFExistence(pdfURL, function(exists) {
                    libstcLink.textContent = exists ? '[PDF] LibSTC' : '[NoPDF] LibSTC';
                    buttonContainer.appendChild(libstcLink);
                });
            }
        },
        onerror: function(error) {
            console.error('Error fetching DOI:', error);
        }
    });
}

function addButtons() {
    const results = document.querySelectorAll('#gs_res_ccl_mid .gs_r.gs_or.gs_scl');

    results.forEach(result => {
        const titleLink = result.querySelector('.gs_rt a');
        const yearElement = result.querySelector('.gs_a');

        if (!titleLink || !yearElement) return;

        const year = extractYear(yearElement);
        let buttonContainer = result.querySelector('.gs_or_ggsm');

        if (!buttonContainer) {
            const div = document.createElement('div');
            div.className = 'gs_ggs gs_fl';
            div.innerHTML = '<div class="gs_ggsd"><div class="gs_or_ggsm"></div></div>';
            result.insertBefore(div, result.firstChild);
            buttonContainer = div.querySelector('.gs_or_ggsm');

            // Add Sci-Hub button with AJAX check
            checkSciHubPDF(titleLink.href, buttonContainer);

            // Add LibGen button with AJAX check
            checkLibGenPDF(titleLink.textContent, buttonContainer);

            // Add LibSTC button only for papers after 2020
            if (year > 2020) {
                fetchDOIAndAddButton(titleLink, buttonContainer);
            }
        }
    });
}

// Initial setup
addButtons();

// Watch for dynamic content changes
const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
        if (mutation.addedNodes.length) {
            addButtons();
        }
    });
});

observer.observe(document.body, {
    childList: true,
    subtree: true
});