Sleazy Fork is available in English.

GBT gallery downloader (GBTGD)

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

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