// ==UserScript== // @name Wallhaven一键下载 // @namespace 这是干啥的,不是很懂 // @version 1.0 // @description 在Wallhaven主页外的每张略缩图上增添下载按钮和勾选框,可以直接点“下载”按钮对原图进行下载,也可以勾选想要下载的图片,然后点击“逐个下载”或“打包下载”。 // @match *://wallhaven.cc/* // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // @license MIT // @author EPC_SG // @downloadURL none // ==/UserScript== (function() { // 添加下载按钮的函数 function addDownloadButtons(thumbnailPage) { // 选择页面中的每个壁纸缩略图 const thumbnails = thumbnailPage.querySelectorAll('.thumb'); // 为每个缩略图添加下载按钮 thumbnails.forEach((thumbnail, index) => { // 获取壁纸ID const wallpaperId = thumbnail.dataset.wallpaperId; if (!wallpaperId) return; // 检查 thumb-info 中是否有 PNG 标签 const isPng = thumbnail.querySelector('.thumb-info .png') !== null; // 根据格式构造原图下载链接 const imageFormat = isPng ? 'png' : 'jpg'; const imageUrl = `https://w.wallhaven.cc/full/${wallpaperId.substring(0, 2)}/wallhaven-${wallpaperId}.${imageFormat}`; // 创建复选框 const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'download-checkbox'; checkbox.style.position = 'absolute'; // 设置为绝对定位 checkbox.style.visibility = 'visible'; // 设置为可见 checkbox.style.zIndex = '1000'; // 确保在最顶层 checkbox.style.top = '15px'; // 根据需要设置位置 checkbox.style.right = '57px'; // 根据需要设置位置 checkbox.style.width = '20px'; checkbox.style.height = '20px'; checkbox.value = imageUrl; // 保存图片链接 checkbox.onclick = updateSelectionRatio; // 创建下载按钮 const button = document.createElement('button'); button.innerText = '下载'; button.style.position = 'absolute'; // 设置为绝对定位 button.style.zIndex = '1000'; // 确保在最顶层 button.style.top = '10px'; // 根据需要设置位置 button.style.right = '5px'; // 根据需要设置位置 button.style.padding = '5px 10px'; button.style.backgroundColor = '#4CAF50'; button.style.color = 'white'; button.style.border = 'none'; button.style.borderRadius = '5px'; button.style.cursor = 'pointer'; button.href = imageUrl; button.download = imageUrl.split('/').pop(); // 添加点击事件,下载图片 button.onclick = function() { var x=new XMLHttpRequest(); const NfileName=button.download; x.open("GET", button.href, true); x.responseType = 'blob'; x.onload=function(e){ var url = window.URL.createObjectURL(x.response); var a = document.createElement('a'); a.href = url; a.download = `${NfileName}`; console.log(`is ${NfileName}`); a.click(); } x.send(); }; thumbnail.appendChild(button); thumbnail.appendChild(checkbox); }); } async function batchDownload() { const checkboxes = Array.from(document.querySelectorAll('.download-checkbox:checked')); // 判断是否有选中的图片 if (checkboxes.length === 0) { progressDisplay.textContent ='未选中任何图片!'; // 提示用户 return; // 直接返回 } for (const checkbox of checkboxes) { const imageUrl = checkbox.value; // 获取图片链接 const fileName = imageUrl.split('/').pop(); // 从链接中提取文件名 // 获取图片的 Blob 数据 const response = await fetch(imageUrl); const blob = await response.blob(); //逐个下载 saveAs(blob, fileName) } } async function packDownload() { const checkboxes = Array.from(document.querySelectorAll('.download-checkbox:checked')); const totalFiles = checkboxes.length; // 选中的文件总数 const zip = new JSZip(); // 创建 JSZip 实例 // 判断是否有选中的图片 if (checkboxes.length === 0) { progressDisplay.textContent ='未选中任何图片!'; // 提示用户 return; // 直接返回 } for (let i = 0; i < totalFiles; i++) { const checkbox = checkboxes[i]; const imageUrl = checkbox.value; // 获取图片链接 const fileName = imageUrl.split('/').pop(); // 从链接中提取文件名 // 获取图片的 Blob 数据 const response = await fetch(imageUrl); const blob = await response.blob(); // 将 Blob 添加到 ZIP 文件中 zip.file(fileName, blob); // 更新进度显示 const progress = Math.round(((i + 1) / totalFiles) * 100); // 计算百分比 progressDisplay.textContent = `正在下载:${progress}% (${i + 1}/${totalFiles})`; // 显示进度 } progressDisplay.textContent = '下载完成,正在打包...'; // 生成 ZIP 文件并提供下载 zip.generateAsync({ type: 'blob', compression: 'STORE'}).then(function(content) { saveAs(content, "images.zip"); // 下载 ZIP 文件 progressDisplay.textContent = '打包完成!'; }); } function toggleSelectAll() { const checkboxes = document.querySelectorAll('.download-checkbox'); const allChecked = Array.from(checkboxes).every(checkbox => checkbox.checked); checkboxes.forEach(checkbox => { checkbox.checked = !allChecked; // 切换状态 }); updateSelectionRatio(); // 更新比例显示 } function updateSelectionRatio() { const checkboxes = document.querySelectorAll('.download-checkbox'); const total = checkboxes.length; const selected = Array.from(checkboxes).filter(checkbox => checkbox.checked).length; ratioDisplay.textContent = `已选择 ${selected} / ${total}`; } const ratioDisplay = document.createElement('div'); const selectAllButton = document.createElement('button'); const batchDownloadButton = document.createElement('button'); const packDownloadButton = document.createElement('button'); const progressDisplay = document.createElement('div'); // 等待页面加载完成 window.addEventListener('load', function() { const searchbar = document.querySelector('.expanded') addDownloadButtons(document.querySelector('.thumb-listing-page')); updateSelectionRatio() // 创建比例显示元素 ratioDisplay.style.color = 'white'; ratioDisplay.style.position = 'relative'; ratioDisplay.style.left = '5px'; searchbar.appendChild(ratioDisplay); // 创建全选按钮 selectAllButton.textContent = '全选/取消'; selectAllButton.onclick = (event) => { event.preventDefault(); // 阻止默认行为 toggleSelectAll(); }; selectAllButton.style.backgroundColor = '#4CAF50'; selectAllButton.style.color = 'white'; selectAllButton.style.padding = '5px 10px'; selectAllButton.style.borderRadius = '5px'; selectAllButton.style.cursor = 'pointer'; selectAllButton.style.position = 'relative'; selectAllButton.style.left = '10px'; searchbar.appendChild(selectAllButton); // 创建逐个下载按钮 batchDownloadButton.textContent = '逐个下载'; batchDownloadButton.onclick = (event) => { event.preventDefault(); // 阻止默认行为 batchDownload(); }; batchDownloadButton.style.backgroundColor = '#4CAF50'; batchDownloadButton.style.color = 'white'; batchDownloadButton.style.padding = '5px 10px'; batchDownloadButton.style.borderRadius = '5px'; batchDownloadButton.style.cursor = 'pointer'; batchDownloadButton.style.position = 'relative'; batchDownloadButton.style.left = '15px'; searchbar.appendChild(batchDownloadButton); // 创建打包下载按钮 packDownloadButton.textContent = '打包下载'; packDownloadButton.onclick = (event) => { event.preventDefault(); // 阻止默认行为 packDownload(); }; packDownloadButton.style.backgroundColor = '#4CAF50'; packDownloadButton.style.color = 'white'; packDownloadButton.style.padding = '5px 10px'; packDownloadButton.style.borderRadius = '5px'; packDownloadButton.style.cursor = 'pointer'; packDownloadButton.style.position = 'relative'; packDownloadButton.style.left = '20px'; searchbar.appendChild(packDownloadButton); // 创建进度显示元素 progressDisplay.style.position = 'relative'; progressDisplay.style.left = '25px'; searchbar.appendChild(progressDisplay); // 监视DOM变化,动态添加按钮 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length) { mutation.addedNodes.forEach((node) => { if (node.classList && node.classList.contains('thumb-listing-page')) { // 调用 addDownloadButtons 处理最新的 thumb-listing-page addDownloadButtons(node); updateSelectionRatio(); } }); } }); }); // 监视父级元素 thumbs const config = { childList: true, subtree: true }; const targetNode = document.querySelector('.thumb-listing'); // 监视 thumb-listing if (targetNode) { observer.observe(targetNode, config); } }); })();