GPT gallery downloader (GPTGD)

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

  1. // ==UserScript==
  2. // @name GPT gallery downloader (GPTGD)
  3. // @namespace _pc
  4. // @version 5.37
  5. // @license MIT
  6. // @description Creates a button to download all images from gallery in a single .zip file.
  7. // @author verydelight
  8. // @match *://*.gayporntube.com/galleries/*
  9. // @connect gayporntube.com
  10. // @connect gayboystube.com
  11. // @icon https://www.gayporntube.com/favicon.ico
  12. // @run-at document-start
  13. // @grant GM.xmlHttpRequest
  14. // @grant GM_download
  15. // @grant GM_xmlHttpRequest
  16. // @grant GM.download
  17. // @require https://update.greasyfork.org/scripts/473358/1237031/JSZip.js
  18. // @require https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js
  19. // ==/UserScript==
  20. 'use strict';
  21. document.addEventListener("DOMContentLoaded", function() {
  22. const zip = new JSZip();
  23. const button = document.createElement("button");
  24. const downloadStatus = document.createElement("div");
  25. const progressElement = document.createElement("progress");
  26. const zipFileName = document.querySelector('h1.title').innerText
  27. .replace(/[^a-zA-Z0-9-_ ]/g, '')
  28. .replace(/\s+/g, ' ')
  29. .substring(0, 245)
  30. .replace(/^_+|_+$/g, '')
  31. .trim();
  32. const finalZipFileName = `GPT ${zipFileName}`;
  33. progressElement.id = "progress";
  34. progressElement.setAttribute("value",0);
  35. progressElement.setAttribute("max",100);
  36. progressElement.style.width = "100%";
  37. button.textContent = "Download Gallery";
  38. button.type = "button";
  39. button.classList.add('bttn');
  40. button.addEventListener ("click", fullSize, false);
  41. document.getElementById('tab_video_info').append(button);
  42. async function downloadFile(url, filename,iCurr,iMax) {
  43. try {
  44. const response = await GM.xmlHttpRequest({
  45. method: 'GET',
  46. responseType: 'blob',
  47. url: url,
  48. headers: {
  49. "Content-Type": "image/jpeg", "Accept": "image/jpeg"
  50. },
  51. });
  52. const blob = new Blob([response.response],{type: 'image/jpeg'});
  53. zip.file(filename, blob, {binary: true})
  54. } catch (err) {
  55. console.error("GPTGD: Error in fetching and downloading file: ",iCurr," " , err);
  56. }
  57. if(iCurr==iMax-1){
  58. downloadStatus.innerHTML = "Now zipping images, creating zip-file and sending for download. Please wait...";
  59. const content = await zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 6 } });
  60. saveAs(content, finalZipFileName);
  61. downloadStatus.innerHTML = "Download complete!";
  62. }
  63. }
  64. async function fullSize() {
  65. button.style.visibility = 'hidden';
  66. document.getElementById('tab_video_info').append(downloadStatus);
  67. document.getElementById('tab_video_info').append(progressElement);
  68. const images = document.querySelectorAll('#album_view_album_view > #tab5 img')
  69. const iMax = images.length;
  70. let downloadTime = 0;
  71. let downloadEstimate = 0;
  72. let downloadTimeFull = 0;
  73. let downloadPercent = 0;
  74. const pattern1 = /\/thumbs/;
  75. const pattern2 = /\/main\/\d{1,4}x\d{1,4}/;
  76. for (let i = 0; i < iMax; i++) {
  77. const timeRemaining = downloadEstimate > 0 ? formatTime(downloadEstimate) : '';
  78. downloadStatus.innerHTML = `Downloading image ${i + 1}/${iMax}${timeRemaining ? ` (ca.: ${timeRemaining} remaining)` : ''}`;
  79. const currentSrc = images[i].getAttribute('data-src').replace(pattern1, '').replace(pattern2, '/sources');
  80. const fileName = currentSrc.split('/').pop();
  81. const downloadStart = Date.now();
  82. await downloadFile(currentSrc, fileName, i, iMax);
  83. const downloadEnd = Date.now();
  84. downloadTime += downloadEnd - downloadStart;
  85. const averageTimePerFile = downloadTime / (i + 1);
  86. downloadEstimate = Math.round((averageTimePerFile * iMax - downloadTime) / 1000);
  87. const downloadTimeFull = Math.round(averageTimePerFile * iMax);
  88. const downloadPercent = Math.round((downloadTime / downloadTimeFull) * 100);
  89. progressElement.setAttribute("value", downloadPercent);
  90. }
  91. }
  92. function formatTime(seconds) {
  93. const minutes = Math.floor(seconds / 60);
  94. const secs = seconds % 60;
  95. const formattedMinutes = minutes > 0 ? (minutes < 10 ? "0" + minutes : minutes) : "00";
  96. const formattedSeconds = secs < 10 ? "0" + secs : secs;
  97. return minutes > 0 ? `${formattedMinutes}:${formattedSeconds} minutes` : `${formattedSeconds} seconds`;
  98. }
  99. });