Pinterest Slideshow (dvirzxc's fork)

Fork from French Bond's Pinterest Slideshow script (ver. 1.5.1) with a bunch of my amateur copilot assisted code. - Start a slideshow on any Pinterest page where there's pins.

  1. // ==UserScript==
  2. // @name Pinterest Slideshow (dvirzxc's fork)
  3. // @namespace https://greasyfork.org/users/1257389
  4. // @version 1.0.05
  5. // @description Fork from French Bond's Pinterest Slideshow script (ver. 1.5.1) with a bunch of my amateur copilot assisted code. - Start a slideshow on any Pinterest page where there's pins.
  6. // @author dvirzxc
  7. // @match https://*.pinterest.com/*
  8. // @match https://*.pinterest.at/*
  9. // @match https://*.pinterest.ca/*
  10. // @match https://*.pinterest.ch/*
  11. // @match https://*.pinterest.cl/*
  12. // @match https://*.pinterest.co.kr/*
  13. // @match https://*.pinterest.co.uk/*
  14. // @match https://*.pinterest.com.au/*
  15. // @match https://*.pinterest.com.mx/*
  16. // @match https://*.pinterest.de/*
  17. // @match https://*.pinterest.dk/*
  18. // @match https://*.pinterest.es/*
  19. // @match https://*.pinterest.fr/*
  20. // @match https://*.pinterest.ie/*
  21. // @match https://*.pinterest.info/*
  22. // @match https://*.pinterest.it/*
  23. // @match https://*.pinterest.jp/*
  24. // @match https://*.pinterest.nz/*
  25. // @match https://*.pinterest.ph/*
  26. // @match https://*.pinterest.pt/*
  27. // @match https://*.pinterest.se/*
  28. // @license MIT
  29. // @grant GM_registerMenuCommand
  30. // @require http://code.jquery.com/jquery-latest.js
  31. // ==/UserScript==
  32.  
  33. /* globals jQuery, $ */
  34.  
  35. $(function () {
  36. 'use strict';
  37.  
  38. let customSlideInterval = 1000; // Default slide interval in milliseconds
  39. let pins = [];
  40. let c = 0; // Current slide number
  41. let interval;
  42. let running = 0;
  43. let observer;
  44.  
  45. function init() {
  46. addSlideShowButton();
  47. addSlideShowImageAndControls();
  48. collectPinsInfo();
  49. observeDynamicChanges();
  50. addKeyboardShortcuts();
  51. addContextMenuOptions();
  52. }
  53.  
  54. function addSlideShowButton() {
  55. $('body').append(
  56. '<div style="position: fixed; bottom: 20px; left: 10px;">' +
  57. '<div>' +
  58. '<span class="slideshow-button" style="cursor:pointer; background-color: #C92228; color: #fff; padding: 8px; font-weight: bold; font-size: 14px; border-radius: 4px;">Slideshow</span>' +
  59. '</div>' +
  60. '</div>'
  61. );
  62.  
  63. $('.slideshow-button').click(startSlideshow);
  64. }
  65.  
  66. function addSlideShowImageAndControls() {
  67. // Add slideshow div
  68. $('body').append(
  69. '<div class="slideshow" style="display:none; position: fixed; width: 100%; height: 100%; background-color: #333; z-index: 10000000; left: 0; top: 0;">' +
  70. '<a id="slideshow-link" target="_blank"><img id="slideshow-img" style="object-fit: contain; width: 100%; height: 100%;"></a>' +
  71. '</div>'
  72. );
  73.  
  74. // Add the slideshow menu
  75. $('.slideshow').append(
  76. '<div class="menu-slideshow" style="position: absolute; left:3px; top:3px; font-size:14px;"></div>'
  77. );
  78.  
  79. $('.menu-slideshow')
  80. .append(
  81. '<div class="stop-slideshow" style="cursor:pointer; background-color: #C92228; color: #fff; padding: 7px; float:left; font-weight: bold; border-radius: 4px;">Stop</div>'
  82. )
  83. .append('<div class="info-slideshow" style="color: #ccc; padding: 7px; float:left;">/</div>');
  84.  
  85. // Handle Stop Button
  86. $('.stop-slideshow').click(stopSlideshow);
  87. }
  88.  
  89. function collectPinsInfo() {
  90. const newPins = getPinsInfo();
  91. newPins.forEach(newPin => {
  92. if (!pins.some(pin => pin.href === newPin.href)) {
  93. pins.push(newPin);
  94. }
  95. });
  96. console.log(pins);
  97. }
  98.  
  99. function observeDynamicChanges() {
  100. observer = new MutationObserver(function (mutations) {
  101. mutations.forEach(function (mutation) {
  102. if (mutation.addedNodes.length) {
  103. collectPinsInfo();
  104. }
  105. });
  106. });
  107.  
  108. var config = {
  109. childList: true,
  110. subtree: true
  111. };
  112.  
  113. observer.observe(document.body, config);
  114. }
  115.  
  116. function getPinsInfo() {
  117. return Array.from(document.querySelectorAll('[data-test-id="pinWrapper"]')).map(function (pinWrapper) {
  118. const a = pinWrapper.querySelector('a');
  119. const imageContainerLink = a.getAttribute('href');
  120. const img = a.querySelector('img');
  121. return {
  122. href: a.getAttribute('href'),
  123. src: findBestQualityImage(a),
  124. imageContainerLink: imageContainerLink
  125. };
  126. });
  127. }
  128.  
  129. function findBestQualityImage(a) {
  130. const img = a.querySelector('img');
  131. if (img) {
  132. const imgSrcSet = img.getAttribute('srcset');
  133. if (imgSrcSet) {
  134. const srcSetArray = imgSrcSet.split(',').map(function (s) {
  135. return s.trim().split(' ');
  136. });
  137. srcSetArray.sort(function (a, b) {
  138. return parseInt(b[1]) - parseInt(a[1]);
  139. });
  140. return srcSetArray[0][0];
  141. } else {
  142. return img.getAttribute('src');
  143. }
  144. }
  145. return null;
  146. }
  147.  
  148. function startSlideshow() {
  149. $('.slideshow').show();
  150.  
  151. console.log('Starting slideshow');
  152. console.log('Number of slides: ' + pins.length);
  153. console.log('Slide interval: ' + customSlideInterval / 1000 + 's');
  154.  
  155. c = 0;
  156. running = 1;
  157. clearInterval(interval);
  158. interval = setInterval(nextSlide, customSlideInterval);
  159. showSlide();
  160. }
  161.  
  162. function showSlide() {
  163. console.log('Current slide: ' + (c + 1));
  164. const pin = pins[c];
  165. $('#slideshow-img').attr('src', pin.src);
  166. $('#slideshow-link').attr('href', pin.imageContainerLink);
  167. $('.info-slideshow').html(c + 1 + '/' + pins.length);
  168. preloadNextSlide();
  169. }
  170.  
  171. async function preloadNextSlide() {
  172. const nextSlide = c + 1;
  173. if (nextSlide > pins.length - 1) return;
  174. const pin = pins[nextSlide];
  175. console.log('Preloading next slide: ' + pin.src);
  176. preloadPictures([pin.src]);
  177. }
  178.  
  179. function preloadPictures(pictureUrls) {
  180. let loaded = 0;
  181. pictureUrls.forEach(function (url) {
  182. const img = new Image();
  183. img.onload = function () {
  184. loaded++;
  185. if (loaded === pictureUrls.length) {
  186. console.log('All images preloaded');
  187. }
  188. };
  189. img.onerror = function () {
  190. loaded++;
  191. console.error('Failed to load image:', url);
  192. };
  193. img.src = url;
  194. });
  195. }
  196.  
  197. function stopSlideshow() {
  198. clearInterval(interval);
  199. running = 0;
  200. $('.slideshow').hide();
  201. console.log('Slideshow stopped');
  202. }
  203.  
  204. function pauseSlideshow() {
  205. clearInterval(interval);
  206. running = 0;
  207. console.log('Slideshow paused');
  208. }
  209.  
  210. function resumeSlideshow() {
  211. if (!running) {
  212. clearInterval(interval);
  213. interval = setInterval(nextSlide, customSlideInterval);
  214. running = 1;
  215. console.log('Slideshow resumed');
  216. }
  217. }
  218.  
  219. function previousSlide() {
  220. c--;
  221. if (c < 0) c = pins.length - 1;
  222. showSlide();
  223. }
  224.  
  225. function nextSlide() {
  226. c++;
  227. if (c > pins.length - 1) c = 0;
  228. showSlide();
  229. }
  230.  
  231. function addKeyboardShortcuts() {
  232. $(document).on('keydown', function (e) {
  233. if (e.key === 'p') {
  234. pauseSlideshow();
  235. } else if (e.key === 'l') {
  236. resumeSlideshow();
  237. } else if (e.key === 'ArrowLeft') {
  238. previousSlide();
  239. } else if (e.key === 'ArrowRight') {
  240. nextSlide();
  241. }
  242. });
  243. }
  244.  
  245. function addContextMenuOptions() {
  246. GM_registerMenuCommand("Set slide interval to 0.5s", function() {
  247. customSlideInterval = 500;
  248. console.log("Slide interval set to 0.5s");
  249. });
  250.  
  251. GM_registerMenuCommand("Set slide interval to 1s", function() {
  252. customSlideInterval = 1000;
  253. console.log("Slide interval set to 1s");
  254. });
  255.  
  256. GM_registerMenuCommand("Set slide interval to 3s", function() {
  257. customSlideInterval = 3000;
  258. console.log("Slide interval set to 3s");
  259. });
  260.  
  261. GM_registerMenuCommand("Set slide interval to 5s", function() {
  262. customSlideInterval = 5000;
  263. console.log("Slide interval set to 5s");
  264. });
  265.  
  266. GM_registerMenuCommand("Set slide interval to 10s", function() {
  267. customSlideInterval = 10000;
  268. console.log("Slide interval set to 10s");
  269. });
  270. }
  271.  
  272. init();
  273. });