NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Sci-Hub downloader
// @namespace https://openuserjs.org/users/clemente
// @match *://*/*
// @grant GM_xmlhttpRequest
// @version 1.11
// @author clemente
// @license MIT
// @description Display a banner to download the article on Sci-Hub if available
// @description:de Zeigen Sie ein Banner an, um den Artikel über Sci-Hub herunterzuladen, falls verfügbar
// @description:es Despliegue un banner para descargar el artículo sobre Sci-Hub si está disponible
// @description:fr Affiche une bannière pour télécharger l'article sur Sci-Hub si disponible
// @description:it Mostra un banner per scaricare l'articolo su Sci-Hub, se disponibile
// @icon https://sci-hub.it.nf/misc/img/favicon.ico
// @connect sci-hub.it.nf
// @inject-into content
// @noframes
// @homepageURL https://openuserjs.org/scripts/clemente/Sci-Hub_downloader
// @supportURL https://openuserjs.org/scripts/clemente/Sci-Hub_downloader/issues
// ==/UserScript==
/* jshint esversion: 6 */
const SCI_HUB_URL = "https://sci-hub.it.nf";
function getUserLanguage() {
const language = (navigator.language || 'en').slice(0, 2);
return ['de', 'en', 'es', 'fr', 'it'].includes(language) ? language : 'en';
}
const downloadMessages = {
de: 'Herunterladen von Sci-Hub',
en: 'Download from Sci-Hub',
es: 'Descargar desde Sci-Hub',
fr: 'Télécharger sur Sci-Hub',
it: 'Scarica da Sci-Hub',
};
const errorMessages = {
de: 'Es gibt einen Fehler bei der Verbindung zu Sci-Hub. Sie sollten die Sci-Hub-Adresse und Ihr Netzwerk überprüfen.',
en: 'There is an error connecting to Sci-Hub. You should check the Sci-Hub address and your network.',
es: 'Hay un error de conexión con Sci-Hub. Deberías comprobar la dirección de Sci-Hub y tu red.',
fr: 'Il y a une erreur de connexion à Sci-Hub. Vous devez vérifier l’adresse de Sci-Hub et votre réseau.',
it: 'C\'è un errore di connessione a Sci-Hub. Dovresti controllare l\'indirizzo di Sci-Hub e la tua rete.',
};
function getDoiReference() {
const detectedDoiMeta = document.querySelector('meta[name="citation_doi"], meta[name="dc.Identifier"][scheme="doi"]');
if (detectedDoiMeta) return detectedDoiMeta.content;
return null;
}
class NetworkError extends Error {
constructor(status, ...params) {
super(...params);
if(Error.captureStackTrace) {
Error.captureStackTrace(this, NetworkError);
}
this.name = 'NetworkError';
if (status) {
this.message = `Website responded with status ${status}.`;
} else {
this.message = "Unknown error.";
}
}
}
function gm_fetch(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function({ status, responseText }) {
if (status < 200 && status >= 300) return reject(new NetworkError(status));
resolve(responseText);
},
onerror: function() { reject(new NetworkError()); },
});
});
}
async function getDownloadLink(doiReference) {
try {
const scihubPageHtml = await gm_fetch(`${SCI_HUB_URL}/${doiReference}`);
const parser = new DOMParser();
const scihubPageDocument = parser.parseFromString(scihubPageHtml, "text/html");
const pdfDownloadLink = scihubPageDocument.querySelector(`a[onclick$="?download=true'"]`);
const match = pdfDownloadLink.getAttribute("onclick").match(/^location\.href='(.*)'$/);
const downloadLink = match[1];
return downloadLink;
} catch (e) {
if (e instanceof NetworkError) {
// If this is a network error, Sci-Hub is probably not working and it should be noted to the user
console.error(e);
return null;
}
const fallbackLink = `${SCI_HUB_URL}/${doiLink}`;
return fallbackLink;
}
}
function buildBanner(isDownloadAvailable) {
const bannerElement = document.createElement('div');
bannerElement.style = `
background-color: ${isDownloadAvailable ? 'orange' : 'red'};
min-height: 40px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 2147483647;
`;
return bannerElement;
}
function buildBannerContent(downloadLink) {
const language = getUserLanguage();
if (!downloadLink) {
const errorMessage = document.createElement('span');
errorMessage.textContent = errorMessages[language];
errorMessage.style = `
color: white;
font-size: 16px;
font-family: arial;
`;
return errorMessage;
}
const downloadLinkElement = document.createElement('a');
downloadLinkElement.textContent = downloadMessages[language];
downloadLinkElement.href = downloadLink;
downloadLinkElement.download = true;
downloadLinkElement.style = `
color: white;
font-size: 16px;
font-family: arial;
`;
return downloadLinkElement;
}
async function displayBannerWhenDoiIsDetected() {
const doiReference = getDoiReference();
if (!doiReference) return;
console.log("DOI detected:", doiReference);
const downloadLink = await getDownloadLink(doiReference);
if (!downloadLink) {
console.log("No download link found.");
} else {
console.log("Download link retrieved:", downloadLink);
}
const banner = buildBanner(downloadLink);
const bannerContent = buildBannerContent(downloadLink);
banner.append(bannerContent);
document.body.prepend(banner);
}
displayBannerWhenDoiIsDetected();