您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download gallery from waifubitches.com
当前为
// ==UserScript== // @name waifubitches.com Downloader // @description Download gallery from waifubitches.com // @namespace chimichanga // @author chimichanga // @icon https://waifubitches.com/favicon.ico // @version 2.0 // @license MIT // @match https://waifubitches.com/gallery/* // @require https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.7.0.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.4/FileSaver.min.js // @noframes // @connect self // @connect userapi.com // @run-at document-idle // @grant GM_xmlhttpRequest // ==/UserScript== // based on the 8muses.com downloader script var downBtn; var downStatus; var zipFile; const State = { NOT_STARTED: Symbol('not_started'), DOWNLOADING: Symbol('downloading'), COMPRESSING: Symbol('compressing'), DONE: Symbol('done') }; var state = State.NOT_STARTED; var zip = new JSZip(); var resultBlob; var sources = []; var thumbnails = []; var completed = 0; var failed = 0; $(document).ready(function () { sources = $('div.grid .grid-item a').map((_, { href }) => href).get(); thumbnails = $('div.grid .grid-item img.img-fluid').map((_, { src }) => src).get(); sources = sources.map((s, i) => [s, thumbnails[i]]); if (sources.length == 0) return; downBtn = $(`<a class="btn btn-sm btn-warning"><i class="bi bi-download"></i> <span>DOWNLOAD (${sources.length})</span></a>`); downStatus = $(downBtn).find('span'); $('body > div:nth-child(1) > div.pb-2 > center > noindex').append(downBtn); zipFile = window.location.href.split('/gallery/')[1] + '+' + $('body > div:nth-child(1) > h1').get(0).innerText + '.zip'; $(downBtn).click(download); }); function updateState(newState, progress) { state = newState; $(downBtn).toggleClass('btn-success', state == State.DONE); $(downBtn).toggleClass('btn-warning', state == State.NOT_STARTED || state == State.COMPRESSING || state == State.DOWNLOADING); $(downBtn).toggleClass('btn-danger', failed > 0); let messages = { [State.NOT_STARTED]: `DOWNLOAD`, [State.DOWNLOADING]: `DOWNLOADING`, [State.COMPRESSING]: `COMPRESSING`, [State.DONE]: `ZIP READY`, } $(downStatus).html(messages[state] + (failed > 0 ? ` (${failed} failed)` : '') + (progress ? ` ${progress}` : '')); } function download() { if (state == State.DONE && failed == 0) { saveZip(); return; } if (state == State.DOWNLOADING || state == State.COMPRESSING) return; updateState(State.DOWNLOADING); completed = 0; failed = 0; Promise.allSettled( sources.map(([url, backup]) => fetch(url).catch((cause) => { console.log(`can't fetch original image, ${cause}: ${url}`); return fetch(backup); }).then(({ response, url }) => { completed++; updateState(State.DOWNLOADING, `${completed}/${sources.length}`); let fileName = url.split(/(?:\/(?:a|impg)\/|\?)/)[1].replaceAll('/', '-'); zip.file(fileName, response); }).catch((cause) => { console.log(`can't fetch thumbnail image, ${cause}: ${url}`); failed++; }))) .then(saveZip); } function fetch(url) { return new Promise((resolve, reject) => GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'arraybuffer', onload: ({ response, status }) => status == 200 ? resolve({ response: response, url: url }) : reject('missing'), onerror: () => reject('error'), onabort: () => reject('abort'), ontimeout: () => reject('timeout'), })); } function saveZip() { if (state == State.DONE) { saveAs(resultBlob, zipFile); return; } zip.generateAsync( { type: 'blob' }, ({ percent }) => updateState(State.COMPRESSING, `${percent.toFixed(2)}%`) ).then(function (blob) { updateState(State.DONE, `${completed}/${sources.length}`); resultBlob = blob; saveAs(resultBlob, zipFile); }); }