您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Creates a button to download all images from gallery in a single .zip file.
// ==UserScript== // @name GPT gallery downloader (GPTGD) // @namespace _pc // @version 5.37 // @license MIT // @description Creates a button to download all images from gallery in a single .zip file. // @author verydelight // @match *://*.gayporntube.com/galleries/* // @connect gayporntube.com // @connect gayboystube.com // @icon https://www.gayporntube.com/favicon.ico // @run-at document-start // @grant GM.xmlHttpRequest // @grant GM_download // @grant GM_xmlHttpRequest // @grant GM.download // @require https://update.greasyfork.org/scripts/473358/1237031/JSZip.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js // ==/UserScript== 'use strict'; document.addEventListener("DOMContentLoaded", function() { const zip = new JSZip(); const button = document.createElement("button"); const downloadStatus = document.createElement("div"); const progressElement = document.createElement("progress"); const zipFileName = document.querySelector('h1.title').innerText .replace(/[^a-zA-Z0-9-_ ]/g, '') .replace(/\s+/g, ' ') .substring(0, 245) .replace(/^_+|_+$/g, '') .trim(); const finalZipFileName = `GPT ${zipFileName}`; progressElement.id = "progress"; progressElement.setAttribute("value",0); progressElement.setAttribute("max",100); progressElement.style.width = "100%"; button.textContent = "Download Gallery"; button.type = "button"; button.classList.add('bttn'); button.addEventListener ("click", fullSize, false); document.getElementById('tab_video_info').append(button); async function downloadFile(url, filename,iCurr,iMax) { try { const response = await GM.xmlHttpRequest({ method: 'GET', responseType: 'blob', url: url, headers: { "Content-Type": "image/jpeg", "Accept": "image/jpeg" }, }); const blob = new Blob([response.response],{type: 'image/jpeg'}); zip.file(filename, blob, {binary: true}) } catch (err) { console.error("GPTGD: Error in fetching and downloading file: ",iCurr," " , err); } if(iCurr==iMax-1){ downloadStatus.innerHTML = "Now zipping images, creating zip-file and sending for download. Please wait..."; const content = await zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 6 } }); saveAs(content, finalZipFileName); downloadStatus.innerHTML = "Download complete!"; } } async function fullSize() { button.style.visibility = 'hidden'; document.getElementById('tab_video_info').append(downloadStatus); document.getElementById('tab_video_info').append(progressElement); const images = document.querySelectorAll('#album_view_album_view > #tab5 img') const iMax = images.length; let downloadTime = 0; let downloadEstimate = 0; let downloadTimeFull = 0; let downloadPercent = 0; const pattern1 = /\/thumbs/; const pattern2 = /\/main\/\d{1,4}x\d{1,4}/; for (let i = 0; i < iMax; i++) { const timeRemaining = downloadEstimate > 0 ? formatTime(downloadEstimate) : ''; downloadStatus.innerHTML = `Downloading image ${i + 1}/${iMax}${timeRemaining ? ` (ca.: ${timeRemaining} remaining)` : ''}`; const currentSrc = images[i].getAttribute('data-src').replace(pattern1, '').replace(pattern2, '/sources'); const fileName = currentSrc.split('/').pop(); const downloadStart = Date.now(); await downloadFile(currentSrc, fileName, i, iMax); const downloadEnd = Date.now(); downloadTime += downloadEnd - downloadStart; const averageTimePerFile = downloadTime / (i + 1); downloadEstimate = Math.round((averageTimePerFile * iMax - downloadTime) / 1000); const downloadTimeFull = Math.round(averageTimePerFile * iMax); const downloadPercent = Math.round((downloadTime / downloadTimeFull) * 100); progressElement.setAttribute("value", downloadPercent); } } function formatTime(seconds) { const minutes = Math.floor(seconds / 60); const secs = seconds % 60; const formattedMinutes = minutes > 0 ? (minutes < 10 ? "0" + minutes : minutes) : "00"; const formattedSeconds = secs < 10 ? "0" + secs : secs; return minutes > 0 ? `${formattedMinutes}:${formattedSeconds} minutes` : `${formattedSeconds} seconds`; } });