楼主
测试帖子
https://linux.sb/topic/11607
// ==UserScript==
// @name LINUX SB文章图片下载
// @namespace http://tampermonkey.net/
// @version 1.1
// @description LINUX SB 文章图片一键下载
// @author 吴老狗
// @match https://linux.sb/*
// @run-at document-idle
// @grant GM_xmlhttpRequest
// @connect *
// ==/UserScript==
(function () {
'use strict';
// 下载间隔(毫秒),避免浏览器因短时间内多次触发下载而拦截
const DOWNLOAD_INTERVAL = 400;
function createButton() {
const userCard = document.querySelector('.user-card');
const a = document.createElement('a');
a.textContent = '下载文章所有图片';
a.className = 'btn-post';
a.addEventListener('click', () => handleClick(a));
userCard.appendChild(a);
}
// 获取图片列表:优先用 cgiDataNew,拿不到就退化为扫描正文里的 <img>
function getImageList() {
const imgs = [...document.querySelectorAll('.post-content img')].map(img => img.src);
return { urls: [...new Set(imgs)], prefix: 'sb_img' };
}
// 根据响应的 Content-Type 推断扩展名,拿不到就退回原 url 里的后缀,最后兜底 jpg
function guessExt(blob, url) {
const map = {
'image/jpeg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/webp': 'webp',
'image/bmp': 'bmp'
};
if (blob.type && map[blob.type]) return map[blob.type];
const m = url.match(/\.(jpe?g|png|gif|webp|bmp)(\?|$)/i);
if (m) return m[1].toLowerCase().replace('jpeg', 'jpg');
return 'jpg';
}
async function downloadImage(url, filename) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'blob',
headers: {
Referer: ''
},
onload(res) {
if (res.status < 200 || res.status >= 300) {
reject(new Error(`HTTP ${res.status}`));
return;
}
const blobUrl = URL.createObjectURL(res.response);
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
setTimeout(() => {
URL.revokeObjectURL(blobUrl);
}, 2000);
resolve();
},
onerror: reject
});
});
}
// async function downloadImage(urll, filenameBase) {
// const url = urll.replace('http://', 'https://');
//
// const res = await fetch(url, {
// referrerPolicy: 'no-referrer'
// });
// if (!res.ok) throw new Error(`HTTP ${res.status}`);
// const blob = await res.blob();
// const ext = guessExt(blob, url);
// const blobUrl = URL.createObjectURL(blob);
// const a = document.createElement('a');
// a.href = blobUrl;
// a.download = `${filenameBase}.${ext}`;
// document.body.appendChild(a);
// a.click();
// a.remove();
// setTimeout(() => URL.revokeObjectURL(blobUrl), 2000);
// }
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function handleClick(btn) {
const { urls, prefix } = getImageList();
if (!urls.length) {
alert('未找到可下载的图片');
return;
}
btn.disabled = true;
const originalText = btn.textContent;
let success = 0;
let fail = 0;
for (let i = 0; i < urls.length; i++) {
btn.textContent = `下载中 ${i + 1}/${urls.length}`;
try {
await downloadImage(urls[i], `${prefix}_${i + 1}`);
success++;
} catch (err) {
fail++;
console.error('图片下载失败:', urls[i], err);
}
// 最后一张不用再等
if (i < urls.length - 1) await sleep(DOWNLOAD_INTERVAL);
}
btn.disabled = false;
btn.textContent = originalText;
alert(`下载完成:成功 ${success} 张,失败 ${fail} 张`);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createButton);
} else {
createButton();
}
})();