Checks IPFS links, styles them, opens in new tab, shows progress (text only) and accessible count.
// ==UserScript==
// @name Anna's Archive IPFS Link Checker
// @namespace https://greasyfork.org/
// @version 1.0
// @description Checks IPFS links, styles them, opens in new tab, shows progress (text only) and accessible count.
// @author Bui Quoc Dung
// @match https://annas-archive.*/ipfs_downloads*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @icon https://annas-archive.org/favicon.ico
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(`
ul.mb-4 > li {
transition: opacity 0.3s ease-in-out, font-weight 0.3s ease-in-out;
color: black;
font-weight: normal;
}
ul.mb-4 > li a {
color: black !important;
text-decoration: none !important;
font-weight: inherit;
}
ul.mb-4 > li span {
color: black;
}
#ipfs-checker-progress-text {
text-align: center;
font-size: 0.95em;
color: black;
font-weight: bold;
margin: 10px 0;
}
`);
const ipfsLists = document.querySelectorAll('ul.mb-4');
if (ipfsLists.length === 0) {
console.log("Anna's Archive IPFS Link Checker: No IPFS lists found.");
return;
}
// Create progress text element only (no progress bar)
const progressTextElement = document.createElement('div');
progressTextElement.id = 'ipfs-checker-progress-text';
progressTextElement.textContent = 'Initializing...';
const mainContent = document.querySelector('main') || document.body;
if (ipfsLists[0].parentNode) {
ipfsLists[0].parentNode.insertBefore(progressTextElement, ipfsLists[0]);
} else {
mainContent.insertBefore(progressTextElement, mainContent.firstChild);
}
let linksToProcess = [];
ipfsLists.forEach(list => {
const listItems = list.querySelectorAll('li');
listItems.forEach(li => {
const linkElement = li.querySelector('a[href^="http"]');
if (linkElement) {
linksToProcess.push({
liElement: li,
linkElement: linkElement,
url: linkElement.href
});
}
});
});
const totalLinks = linksToProcess.length;
let checkedLinks = 0;
let accessibleLinks = 0;
if (totalLinks === 0) {
progressTextElement.textContent = "No links found to check.";
return;
}
function updateProgressText() {
checkedLinks++;
if (checkedLinks < totalLinks) {
progressTextElement.textContent = `Checking ${checkedLinks} of ${totalLinks} links...`;
} else {
let finalMessage = `All ${totalLinks} links checked!`;
if (accessibleLinks > 0) {
finalMessage += ` ${accessibleLinks} accessible.`;
} else {
finalMessage += ` None accessible.`;
}
progressTextElement.textContent = finalMessage;
}
}
progressTextElement.textContent = `Checking 0 of ${totalLinks} links...`;
linksToProcess.forEach(item => {
const { liElement, linkElement, url } = item;
linkElement.target = '_blank';
liElement.style.opacity = '0.6';
GM_xmlhttpRequest({
method: 'HEAD',
url: url,
timeout: 15000,
onload: function (response) {
if (response.status >= 200 && response.status < 400) {
liElement.style.fontWeight = 'bold';
liElement.style.opacity = '0.9';
linkElement.style.color = 'black';
linkElement.style.textDecoration = 'none';
linkElement.style.fontWeight = 'inherit';
accessibleLinks++;
} else {
liElement.style.fontWeight = 'normal';
}
updateProgressText();
},
onerror: function () {
liElement.style.fontWeight = 'normal';
updateProgressText();
},
ontimeout: function () {
liElement.style.fontWeight = 'normal';
updateProgressText();
}
});
});
})();