您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
WEBのダウンロードライブラリ
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/528949/1558024/LibImgDown.js
/* * Dependencies: * GM_info(optional) * Docs: https://violentmonkey.github.io/api/gm/#gm_info * GM_xmlhttpRequest(optional) * Docs: https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest * JSZIP * Github: https://github.com/Stuk/jszip * CDN: https://unpkg.com/[email protected]/dist/jszip.min.js * FileSaver * Github: https://github.com/eligrey/FileSaver.js * CDN: https://unpkg.com/[email protected]/dist/FileSaver.min.js */ ; const ImageDownloader = (({ JSZip, saveAs }) => { let maxNum = 0; let promiseCount = 0; let fulfillCount = 0; let isErrorOccurred = false; let createFolder = false; let folderName = "images"; let zipFileName = "download.zip"; let zip = null; // ZIPオブジェクトの初期化 let imageDataArray = []; //imageDataArrayの初期化 // elements let startNumInputElement = null; let endNumInputElement = null; let downloadButtonElement = null; let panelElement = null; let folderRadioYes = null; let folderRadioNo = null; let folderNameInput = null; let zipFileNameInput = null; // svg icons const externalLinkSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentcolor" width="16" height="16"><path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path></svg>`; const reloadSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentcolor" width="16" height="16"><path fill-rule="evenodd" d="M8 2.5a5.487 5.487 0 00-4.131 1.869l1.204 1.204A.25.25 0 014.896 6H1.25A.25.25 0 011 5.75V2.104a.25.25 0 01.427-.177l1.38 1.38A7.001 7.001 0 0114.95 7.16a.75.75 0 11-1.49.178A5.501 5.501 0 008 2.5zM1.705 8.005a.75.75 0 01.834.656 5.501 5.501 0 009.592 2.97l-1.204-1.204a.25.25 0 01.177-.427h3.646a.25.25 0 01.25.25v3.646a.25.25 0 01-.427.177l-1.38-1.38A7.001 7.001 0 011.05 8.84a.75.75 0 01.656-.834z"></path></svg>`; // initialization function init({ maxImageAmount, getImagePromises, title = `package_${Date.now()}`, WidthText = 0, HeightText = 0, imageSuffix = 'jpg', zipOptions = {}, positionOptions = {} }) { // assign value maxNum = maxImageAmount; // setup UI setupUI(positionOptions, title, WidthText, HeightText); // setup update notification setupUpdateNotification(); // add click event listener to download button downloadButtonElement.onclick = function () { if (!isOKToDownload()) return; this.disabled = true; this.textContent = "Processing"; this.style.backgroundColor = '#aaa'; this.style.cursor = 'not-allowed'; download(getImagePromises, title, imageSuffix, zipOptions); }; } // setup UI function setupUI(positionOptions, title, WidthText, HeightText) { title = sanitizeFileName(title); // common input element style const inputElementStyle = ` box-sizing: content-box; padding: 0px 0px; width: 40%; height: 26px; border: 1px solid #aaa; border-radius: 4px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; text-align: center; `; // create start number input element startNumInputElement = document.createElement('input'); startNumInputElement.id = 'ImageDownloader-StartNumInput'; startNumInputElement.style = inputElementStyle; startNumInputElement.type = 'text'; startNumInputElement.value = 1; // create end number input element endNumInputElement = document.createElement('input'); endNumInputElement.id = 'ImageDownloader-EndNumInput'; endNumInputElement.style = inputElementStyle; endNumInputElement.type = 'text'; endNumInputElement.value = maxNum; // prevent keyboard input from being blocked startNumInputElement.onkeydown = (e) => e.stopPropagation(); endNumInputElement.onkeydown = (e) => e.stopPropagation(); // create 'to' span element const toSpanElement = document.createElement('span'); toSpanElement.id = 'ImageDownloader-ToSpan'; toSpanElement.textContent = 'to'; toSpanElement.style = ` margin: 0 6px; color: black; line-height: 1; word-break: keep-all; user-select: none; `; // create download button element downloadButtonElement = document.createElement('button'); downloadButtonElement.id = 'ImageDownloader-DownloadButton'; downloadButtonElement.textContent = 'Download'; downloadButtonElement.style = ` margin-top: 8px; margin-left: auto; // 追加 width: 128px; height: 48px; padding: 5px 5px; display: block; justify-content: center; align-items: center; font-size: 14px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; color: #fff; line-height: 1.2; background-color: #0984e3; border: none; border-radius: 4px; cursor: pointer; `; const toggleButton = document.createElement('button'); toggleButton.id = 'ImageDownloader-ToggleButton'; // toggleButton.textContent = 'UI OPEN'; toggleButton.style = ` position: fixed; top: 45px; left: 5px; z-index: 999999999; padding: 2px 5px; font-size: 14px; font-weight: 'bold'; font-family: 'Monaco', 'Microsoft YaHei'; color: #fff; background-color: #000000; border: 1px solid #aaa; border-radius: 4px; cursor: pointer; `; document.body.appendChild(toggleButton); let isUIVisible = false; // 初期状態を非表示に設定 function toggleUI() { if (isUIVisible) { panelElement.style.display = 'none'; toggleButton.textContent = 'UI OPEN'; } else { panelElement.style.display = 'flex'; toggleButton.textContent = 'UI CLOSE'; } isUIVisible = !isUIVisible; } toggleButton.addEventListener('click', toggleUI) // create range input container element const rangeInputContainerElement = document.createElement('div'); rangeInputContainerElement.id = 'ImageDownloader-RangeInputContainer'; rangeInputContainerElement.style = ` display: flex; justify-content: center; align-items: baseline; `; // create range input container element const rangeInputRadioElement = document.createElement('div'); rangeInputRadioElement.id = 'ImageDownloader-RadioChecker'; rangeInputRadioElement.style = ` display: flex; justify-content: center; align-items: baseline; `; // create panel element panelElement = document.createElement('div'); panelElement.id = 'ImageDownloader-Panel'; panelElement.style = ` position: fixed; top: 80px; left: 5px; z-index: 999999999; box-sizing: border-box; padding: 0px; width: auto; min-width: 200px; max-width: 300px; height: auto; display: none; flex-direction: column; justify-content: center; align-items: baseline; font-size: 12px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; letter-spacing: normal; background-color: #f1f1f1; border: 1px solid #aaa; border-radius: 4px; `; // modify panel position according to 'positionOptions' for (const [key, value] of Object.entries(positionOptions)) { if (key === 'top' || key === 'bottom' || key === 'left' || key === 'right') { panelElement.style[key] = value; } } // create folder radio buttons folderRadioYes = document.createElement('input'); folderRadioYes.type = 'radio'; folderRadioYes.name = 'createFolder'; folderRadioYes.value = 'yes'; folderRadioYes.id = 'createFolderYes'; folderRadioNo = document.createElement('input'); folderRadioNo.type = 'radio'; folderRadioNo.name = 'createFolder'; folderRadioNo.value = 'no'; folderRadioNo.id = 'createFolderNo'; folderRadioNo.checked = true; // フォルダ名入力欄の作成 folderNameInput = document.createElement('textarea'); folderNameInput.id = 'folderNameInput'; folderNameInput.value = title; // titleを初期値として使用 folderNameInput.disabled = true; folderNameInput.style = ` ${inputElementStyle} resize: vertical; height: auto; width: 99%; min-height: 45px; max-height: 200px; padding: 0px 0px; border: 1px solid #aaa; border-radius: 1px; font-size: 11px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; text-align: left; `; // ZIPファイル名入力欄の作成 zipFileNameInput = document.createElement('textarea'); zipFileNameInput.id = 'zipFileNameInput'; zipFileNameInput.value = `${title}.zip`; // titleを使用してZIPファイル名を設定 zipFileNameInput.style = ` ${inputElementStyle} resize: vertical; height: auto; width: 99%; min-height: 45px; max-height: 200px; padding: 0px 0px; border: 1px solid #aaa; border-radius: 1px; font-size: 11px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; text-align: left; `; // add event listeners for radio buttons folderRadioYes.addEventListener('change', () => { createFolder = true; folderNameInput.disabled = false; }); folderRadioNo.addEventListener('change', () => { createFolder = false; folderNameInput.disabled = true; }); // assemble and then insert into document rangeInputContainerElement.appendChild(startNumInputElement); rangeInputContainerElement.appendChild(toSpanElement); rangeInputContainerElement.appendChild(endNumInputElement); panelElement.appendChild(rangeInputContainerElement); rangeInputRadioElement.appendChild(document.createTextNode('フォルダ:')); rangeInputRadioElement.appendChild(folderRadioYes); rangeInputRadioElement.appendChild(document.createTextNode('作成 ')); rangeInputRadioElement.appendChild(folderRadioNo); rangeInputRadioElement.appendChild(document.createTextNode('不要')); panelElement.appendChild(rangeInputRadioElement); panelElement.appendChild(document.createTextNode('フォルダ名: ')); panelElement.appendChild(folderNameInput); panelElement.appendChild(document.createElement('br')); panelElement.appendChild(document.createTextNode('ZIPファイル名: ')); panelElement.appendChild(zipFileNameInput); panelElement.appendChild(document.createTextNode(` サイズ: ${WidthText} x `)); panelElement.appendChild(document.createTextNode(`${HeightText}`)); panelElement.appendChild(downloadButtonElement); document.body.appendChild(panelElement); } // setup update notification async function setupUpdateNotification() { if (typeof GM_info === 'undefined' || typeof GM_xmlhttpRequest === 'undefined') return; // get local version const localVersion = Number(GM_info.script.version); // get latest version //const scriptID = (GM_info.script.homepageURL || GM_info.script.homepage).match(/scripts\/(?<id>\d+)-/)?.groups?.id; let scriptID = null; const homepageURL = GM_info.script.homepageURL || GM_info.script.homepage; if (homepageURL) { const match = homepageURL.match(/scripts\/(?<id>\d+)-/); if (match && match.groups && match.groups.id) { scriptID = match.groups.id; } } const scriptURL = `https://update.gf.qytechs.cn/scripts/${scriptID}/raw.js`; const latestVersionString = await new Promise(resolve => { GM_xmlhttpRequest({ method: 'GET', url: scriptURL, responseType: 'text', onload: res => resolve(res.response.match(/@version\s+(?<version>[0-9\.]+)/)?.groups?.version) }); }); const latestVersion = Number(latestVersionString); if (Number.isNaN(localVersion) || Number.isNaN(latestVersion)) return; if (latestVersion <= localVersion) return; // show update notification const updateLinkElement = document.createElement('a'); updateLinkElement.id = 'ImageDownloader-UpdateLink'; updateLinkElement.href = scriptURL.replace('raw.js', 'raw.user.js'); updateLinkElement.innerHTML = `Update to V${latestVersionString}${externalLinkSVG}`; updateLinkElement.style = ` position: absolute; bottom: -38px; left: -1px; display: flex; justify-content: space-around; align-items: center; box-sizing: border-box; padding: 8px; width: 146px; height: 32px; font-size: 14px; font-family: 'Consolas', 'Monaco', 'Microsoft YaHei'; text-decoration: none; color: white; background-color: #32CD32; border-radius: 4px; `; updateLinkElement.onclick = () => setTimeout(() => { updateLinkElement.removeAttribute('href'); updateLinkElement.innerHTML = `Please Reload${reloadSVG}`; updateLinkElement.style.cursor = 'default'; }, 1000); panelElement.appendChild(updateLinkElement); } // check validity of page nums from input function isOKToDownload() { const startNum = Number(startNumInputElement.value); const endNum = Number(endNumInputElement.value); if (Number.isNaN(startNum) || Number.isNaN(endNum)) { alert("正しい値を入力して\nPlease enter page number correctly."); return false; } if (!Number.isInteger(startNum) || !Number.isInteger(endNum)) { alert("正しい値を入力して\nPlease enter page number correctly."); return false; } if (startNum < 1 || endNum < 1) { alert("ページ番号の値を1より小さくすることはできません1\nPage number should not smaller than 1."); return false; } if (startNum > maxNum || endNum > maxNum) { alert(`ページ番号の値を1より大きくすることはできません${maxNum}\nPage number should not bigger than ${maxNum}.`); return false; } if (startNum > endNum) { alert("開始ページ番号の値を終了ページ番号の値より大きくすることはできません\nNumber of start should not bigger than number of end."); return false; } return true; } // start downloading async function download(getImagePromises, title, imageSuffix, zipOptions) { const startNum = Number(startNumInputElement.value); const endNum = Number(endNumInputElement.value); promiseCount = endNum - startNum + 1; // start downloading images, max amount of concurrent requests is limited to 4 let images = []; for (let num = startNum; num <= endNum; num += 4) { const from = num; const to = Math.min(num + 3, endNum); try { const result = await Promise.all(getImagePromises(from, to)); images = images.concat(result); } catch (error) { return; // cancel downloading } } // configure file structure of zip archive JSZip.defaults.date = new Date(Date.now() - (new Date()).getTimezoneOffset() * 60000); zip = new JSZip(); const { folderName, zipFileName } = sanitizeInputs(folderNameInput, zipFileNameInput); if (createFolder) { const folder = zip.folder(folderName); for (const [index, image] of images.entries()) { const filename = `${String(index + 1).padStart(3, '0')}.${imageSuffix}`; folder.file(filename, image, zipOptions); } } else { for (const [index, image] of images.entries()) { const filename = `${String(index + 1).padStart(3, '0')}.${imageSuffix}`; zip.file(filename, image, zipOptions); } } // start zipping & show progress const zipProgressHandler = (metadata) => { downloadButtonElement.innerHTML = `Zipping(${metadata.percent.toFixed()}%)`; }; const content = await zip.generateAsync({ type: "blob" }, zipProgressHandler); // open 'Save As' window to save saveAs(content, zipFileName); // 全て完了 downloadButtonElement.textContent = "Completed"; // ボタンを再度押せるようにする } // ファイル名整形用の関数 function sanitizeFileName(str) { return str.trim() // 全角英数字を半角に変換 .replace(/[A-Za-z0-9]/g, s => String.fromCharCode(s.charCodeAt(0) - 0xFEE0)) // 連続する空白(全角含む)を半角スペース1つに統一 .replace(/[\s\u3000]+/g, ' ') // 「!?」または「?!」を「⁉」に置換 .replace(/[!?][!?]/g, '⁉') // 特定の全角記号を対応する半角記号に変換 .replace(/[!#$%&’,.()+-=@^_{}]/g, s => { const from = '!#$%&’,.()+-=@^_{}'; const to = "!#$%&',.()+-=@^_{}"; return to[from.indexOf(s)]; }) // ファイル名に使えない文字をハイフンに置換 .replace(/[\\/:*?"<>|]/g, '-'); } // folderNameとzipFileNameの整形処理関数 function sanitizeInputs(folderNameInput, zipFileNameInput) { const folderName = sanitizeFileName(folderNameInput.value); const zipFileName = sanitizeFileName(zipFileNameInput.value); return { folderName, zipFileName }; } // handle promise fulfilled function fulfillHandler(res) { if (!isErrorOccurred) { fulfillCount++; downloadButtonElement.innerHTML = `Processing(${fulfillCount}/${promiseCount})`; } return res; } // handle promise rejected function rejectHandler(err) { isErrorOccurred = true; console.error(err); downloadButtonElement.textContent = 'Error Occurred'; downloadButtonElement.style.backgroundColor = 'red'; return Promise.reject(err); } function reset() { maxNum = 0; promiseCount = 0; fulfillCount = 0; isErrorOccurred = false; zip = null; // ZIPオブジェクトの初期化 imageDataArray = []; //imageDataArrayの初期化 // UI要素のリセット if (startNumInputElement) startNumInputElement.value = 1; if (endNumInputElement) endNumInputElement.value = 1; if (downloadButtonElement) { downloadButtonElement.disabled = false; downloadButtonElement.textContent = "Download"; downloadButtonElement.style.backgroundColor = '#0984e3'; downloadButtonElement.style.cursor = 'pointer'; } // パネルの削除(再作成のため) if (panelElement && panelElement.parentNode) { panelElement.parentNode.removeChild(panelElement); } } return { init, fulfillHandler, rejectHandler }; })(window);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址