// ==UserScript== // @name Google Scholar to Sci-Hub // @namespace ScholarToSciHub // @version 1.3 // @description Adds Sci-Hub, LibGen, and LibSTC buttons to Google Scholar results // @author Bui Quoc Dung (modified) // @include https://scholar.google.*/* // @license AGPL-3.0-or-later // @grant GM_xmlhttpRequest // @downloadURL none // ==/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) { // Parse the response text into a DOM object const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); // Check if there is any element with the class 'table table-striped' const hasTable = doc.querySelector('.table.table-striped') !== null; const libGenLink = document.createElement('a'); libGenLink.textContent = hasTable ? '[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 fetchDOIAndAddButtonLibSTC(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 = '
'; 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) { fetchDOIAndAddButtonLibSTC(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 });