GPT gallery downloader (GPTGD)

Creates a button to download all images from gallery in a single .zip file.

Verzia zo dňa 09.03.2025. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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`;
	}
});