Reddl

Reddit Preview Ctrl+s Downloading Utility - also applies to a few other sites - Only tested on Firefox

  1. // ==UserScript==
  2. // @name Reddl
  3. // @version 0.2.4
  4. // @description Reddit Preview Ctrl+s Downloading Utility - also applies to a few other sites - Only tested on Firefox
  5. // @author JFKennedy
  6. // @license MIT
  7. // @match https://*.reddit.com/media?url=*
  8. // @match https://w.wallha.com/*
  9. // @match https://get.wallhere.com/*
  10. // @match https://c.wallhere.com/*
  11. // @match https://*.wallhere.com/*
  12. // @match https://*.pinterest.com/*
  13. // @match https://*.pinterest.co.uk/*
  14. // below this line match only the ones that are direct img serving!!
  15. // @match https://wallpapersmug.com/download/*
  16. // @match https://wallpapersmug.com/large/*
  17. // @match https://1.bp.blogspot.com/*/*
  18. // @match https://p*.wallpaperbetter.com/*/*
  19. // @match https://*.pinimg.com/*
  20. // @match https://images*.alphacoders.com/*
  21. // @match https://wallpaperaccess.com/full/*
  22. // @match https://www.wallpaperaccess.com/full/*
  23. // @match https://*.deviantart.com/*
  24. // @icon https://www.google.com/s2/favicons?sz=64&domain=reddit.com
  25. // @grant GM_xmlhttpRequest
  26. // @connect external-preview.redd.it
  27. // @connect i.redd.it
  28. // @connect *.redd.it
  29. // @connect *.reddit.com
  30. // @connect w.wallha.com
  31. // @connect get.wallhere.com
  32. // @connect c.wallhere.com
  33. // @connect *.pinimg.com
  34. // @connect i.pinimg.com
  35. // @connect wixmp.com
  36. // @namespace https://greasyfork.org/users/1202256
  37. // ==/UserScript==
  38.  
  39. document.addEventListener('keydown', async e => {
  40. const fetchWithTampermonkey = async (url, responseType = 'blob') => {
  41. return new Promise((resolve, reject) => {
  42. const loader = document.createElement('div');
  43. loader.style.height = '4px';
  44. loader.style.width = '100vw';
  45. loader.style.position = 'fixed';
  46. loader.style.top = '0';
  47. loader.style.left = '0';
  48. loader.style.display = 'block';
  49. loader.style.background = '#1a1a1a00';
  50. const loaderInner = document.createElement('div');
  51. loaderInner.style.height = loader.style.height;
  52. loaderInner.style.width = '0';
  53. loaderInner.style.position = 'fixed';
  54. loaderInner.style.top = '0';
  55. loaderInner.style.left = '0';
  56. loaderInner.style.display = 'block';
  57. loaderInner.style.background = '#aa99ff';
  58. loader.appendChild(loaderInner);
  59. const loaderInner2 = document.createElement('div');
  60. loaderInner2.style.height = loader.style.height;
  61. loaderInner2.style.width = loader.style.height;
  62. loaderInner2.style.position = 'fixed';
  63. loaderInner2.style.top = '0';
  64. loaderInner2.style.right = '0';
  65. loaderInner2.style.display = 'block';
  66. loaderInner2.style.filter = 'blur(2px)';
  67. loaderInner2.style.background = loaderInner.style.background;
  68. loaderInner2.style.borderRadius = '128px';
  69. loaderInner.appendChild(loaderInner2);
  70. document.body.appendChild(loader);
  71. GM_xmlhttpRequest({
  72. method: 'GET',
  73. url,
  74. responseType, // specify the desired responseType
  75. onload: (response) => {
  76. if (response.status === 200) {
  77. if (responseType === 'blob') {
  78. resolve(new Blob([response.response], { type: response.responseHeaders.match(/content-type: (.*)/i)[1] }));
  79. } else {
  80. resolve(response.responseText);
  81. }
  82. loader.remove();
  83. } else {
  84. setTimeout(() => loader.remove(), 1000)
  85. loaderInner.style.background = '#ffaaaa'
  86. loaderInner2.style.background = '#ffaaaa'
  87. reject(new Error('Request failed with status ' + response.status));
  88. }
  89. },
  90. onprogress: function (progress) {
  91. const percent = progress.lengthComputable ? (progress.position / progress.totalSize) : 100;
  92. console.log(progress, percent);
  93. loaderInner.style.width = `${percent * 100}vw`
  94. loaderInner2.style.left = loaderInner;
  95. },
  96. onerror: (error) => {
  97. reject(error);
  98. },
  99. });
  100. });
  101. };
  102. const dl = async (url, filename) => {
  103. const data = await fetchWithTampermonkey(url);
  104. const dlUrl = URL.createObjectURL(data);
  105. const a = document.createElement('a');
  106. a.href = dlUrl;
  107. a.download = filename || ('img-' + Date.now());
  108. document.body.appendChild(a);
  109. a.click();
  110. a.remove();
  111. }
  112.  
  113.  
  114. if (e.key === 's' && e.ctrlKey) {
  115. switch (location.hostname) {
  116. case 'reddit.com':
  117. case 'redd.it':
  118. case 'www.reddit.com':
  119. case 'www.redd.it':
  120. case 'i.redd.it':
  121. {
  122. const url = new URLSearchParams(location.search).get('url')
  123. if (!url) return;
  124. e.preventDefault()
  125. const fname = (url.split('-').pop() || '').split('.');
  126. fname.pop();
  127. dl(url, `reddit-${fname.join('.') || `unknown-${Date.now()}`}.${url.split('.').pop()}`);
  128. break;
  129. }
  130. case 'wallhere.com':
  131. case 'www.wallhere.com':
  132. {
  133. const img = document.querySelector('img[itemprop="contentURL"]')
  134. if (!img) return;
  135. const url = img.src;
  136. if (!url) return;
  137. e.preventDefault()
  138. const fname = (url.split('-').pop() || '').split('.');
  139. fname.pop();
  140. dl(dlUrl, `wall-${fname.join('.') || `unknown-${Date.now()}`}.${url.split('.').pop()}`);
  141. break;
  142. }
  143. case 'i.pinimg.com':
  144. case 'pinimg.com':
  145. case 'w.wallha.com':
  146. case 'i.wallha.com':
  147. case 'get.wallhere.com':
  148. case 'c.wallhere.com':
  149. {
  150. const img = document.querySelector('img')
  151. if (!img) return console.warn('No image');
  152. e.preventDefault()
  153. dl(img.src);
  154. break;
  155. }
  156. case 'pinterest.com':
  157. case 'pinterest.co.uk':
  158. case 'www.pinterest.com':
  159. case 'www.pinterest.co.uk':
  160. {
  161. const img = document.querySelector('img[src*="pinimg.com"][loading="auto"]')
  162. if (!img) return console.warn('No image');
  163. e.preventDefault()
  164. dl(img.src);
  165. break;
  166. }
  167. case 'deviantart.com':
  168. case 'www.deviantart.com':
  169. {
  170. const img = document.querySelector('img[property="contentUrl"][src*="wixmp.com"]')
  171. if (!img) return console.warn('No image');
  172. e.preventDefault()
  173. dl(img.src);
  174. break;
  175. }
  176. default:
  177. {
  178. const img = document.querySelector('body > img:first-child')
  179. if (img) {
  180. e.preventDefault();
  181. dl(img.src);
  182. }
  183. }
  184. }
  185. }
  186. })
  187.