您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds Sci-Hub, LibGen, LibSTC and Anna's Archive buttons to Google Scholar results
当前为
// ==UserScript== // @name Google Scholar to free PDFs // @namespace ScholarToSciHub // @version 1.7 // @description Adds Sci-Hub, LibGen, LibSTC and Anna's Archive buttons to Google Scholar results // @author Bui Quoc Dung // @match https://scholar.google.*/* // @license AGPL-3.0-or-later // @grant GM_xmlhttpRequest // ==/UserScript== // Define base URLs for different sources const SCIHUB_URL = 'https://www.tesble.com/'; const LIBGEN_URL = 'https://libgen.li/index.php?req='; const LIBSTC_BASE_URL = 'https://hub.libstc.cc/'; const ANNA_URL = 'https://annas-archive.org/scidb/'; const ANNA_CHECK_URL = 'https://annas-archive.org/search?index=journals&q='; // Regular expression to extract DOI from text const DOI_REGEX = /\b(10\.\d{4,}(?:\.\d+)*\/(?:(?!["&'<>])\S)+)\b/gi; // Function to add a loading indicator to the button container function addLoadingIndicator(buttonContainer) { const span = document.createElement('div'); span.textContent = 'Loading...'; span.style.marginBottom = '4px'; span.style.color = 'gray'; span.style.fontSize = '15px'; buttonContainer.appendChild(span); return span; } // Function to update the loading indicator with a clickable link function updateLink(span, textContent, href, isNo = false) { const link = document.createElement('a'); link.textContent = textContent; link.href = href; link.target = '_blank'; link.style.fontSize = '15px'; link.innerHTML = textContent.replace('[PDF]', '<b>[PDF]</b>') if (isNo) { link.style.color = 'gray'; // Màu giống với "Loading..." } span.replaceWith(link); } // Function to extract the year from a given element function extractYear(element) { const yearMatch = element.textContent.match(/\d{4}/); return yearMatch ? parseInt(yearMatch[0]) : 0; } // Function to check if a PDF is available on LibGen function checkLibGenPDF(title, buttonContainer) { const span = addLoadingIndicator(buttonContainer); GM_xmlhttpRequest({ method: 'GET', url: LIBGEN_URL + encodeURIComponent(title), onload: function(response) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); const hasTable = doc.querySelector('.table.table-striped') !== null; updateLink(span, hasTable ? '[PDF] LibGen' : '[No] LibGen', LIBGEN_URL + encodeURIComponent(title), !hasTable); }, onerror: function() { updateLink(span, '[No] LibGen', LIBGEN_URL + encodeURIComponent(title), true); } }); } // Function to check if a PDF is available on Sci-Hub function checkSciHubPDF(url, buttonContainer) { const span = addLoadingIndicator(buttonContainer); GM_xmlhttpRequest({ method: 'GET', url: SCIHUB_URL + url, onload: function(response) { const hasPDF = /iframe|pdf|embed/.test(response.responseText); updateLink(span, hasPDF ? '[PDF] Sci-Hub' : '[No] Sci-Hub', SCIHUB_URL + url, !hasPDF); }, onerror: function() { updateLink(span, '[No] Sci-Hub', SCIHUB_URL + url, true); } }); } // Function to check if a PDF is available on Anna's Archive function checkAnnaPDF(doi, buttonContainer) { const span = addLoadingIndicator(buttonContainer); const checkUrl = ANNA_CHECK_URL + encodeURIComponent(doi); GM_xmlhttpRequest({ method: 'GET', url: checkUrl, onload: function(response) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); const hasPDF = doc.querySelector('.mt-4.uppercase.text-xs.text-gray-500') !== null; updateLink(span, hasPDF ? '[PDF] Anna' : '[No] Anna', ANNA_URL + doi, !hasPDF); }, onerror: function() { updateLink(span, '[No] Anna', ANNA_URL + doi, true); } }); } // Function to check if a PDF is available on LibSTC function checkLibSTCPDF(doi, buttonContainer) { const span = addLoadingIndicator(buttonContainer); const pdfURL = LIBSTC_BASE_URL + doi + '.pdf'; GM_xmlhttpRequest({ method: 'HEAD', url: pdfURL, onload: function(response) { const isPDF = response.status === 200 && response.responseHeaders.toLowerCase().includes('application/pdf'); updateLink(span, isPDF ? '[PDF] LibSTC' : '[No] LibSTC', pdfURL, !isPDF); }, onerror: function() { updateLink(span, '[No] LibSTC', pdfURL, true); } }); } // Function to fetch DOI from a given article link function fetchDOI(titleLink, callback) { GM_xmlhttpRequest({ method: 'GET', url: titleLink.href, onload: function(response) { const matches = response.responseText.match(DOI_REGEX); callback(matches && matches.length ? matches[0] : null); }, onerror: function() { callback(null); } }); } // Function to add buttons to each Google Scholar result function addButtons() { document.querySelectorAll('#gs_res_ccl_mid .gs_r.gs_or.gs_scl').forEach(result => { const titleLink = result.querySelector('.gs_rt a'); const yearElement = result.querySelector('.gs_a'); if (!titleLink || !yearElement) return; 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'); checkSciHubPDF(titleLink.href, buttonContainer); checkLibGenPDF(titleLink.textContent, buttonContainer); fetchDOI(titleLink, (doi) => { if (doi) { checkAnnaPDF(doi, buttonContainer); if (extractYear(yearElement) > 2020) { checkLibSTCPDF(doi, buttonContainer); } } }); } }); } // Initial call to add buttons to existing results addButtons(); // Observe changes in the page to dynamically add buttons when new results appear new MutationObserver((mutations) => { mutations.forEach((mutation) => mutation.addedNodes.length && addButtons()); }).observe(document.body, {childList: true, subtree: true});