Enhanced Catbox File Uploader with Auto-Close Drop Zone and Duration Option

Upload files to Catbox with URL history, customizable duration, minimize support, middle-click to open in new tab, auto-close drop zone if no file is dropped within 3 seconds.

目前为 2024-11-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Enhanced Catbox File Uploader with Auto-Close Drop Zone and Duration Option
  3. // @namespace https://catbox.moe/
  4. // @version 1.1
  5. // @description Upload files to Catbox with URL history, customizable duration, minimize support, middle-click to open in new tab, auto-close drop zone if no file is dropped within 3 seconds.
  6. // @match *://litterbox.catbox.moe/*
  7. // @match *://catbox.moe/*
  8. // @match *://*/*
  9. // @grant GM_addStyle
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // ==/UserScript==
  13. (function() {
  14. 'use strict';
  15.  
  16. // Create necessary DOM elements
  17. const uploadButton = document.createElement('div');
  18. uploadButton.id = 'uploadButton';
  19. uploadButton.innerHTML = '⬆';
  20. document.body.appendChild(uploadButton);
  21.  
  22. const fileInput = document.createElement('input');
  23. fileInput.type = 'file';
  24. fileInput.style.display = 'none';
  25. document.body.appendChild(fileInput);
  26.  
  27. const urlTextBox = document.createElement('input');
  28. urlTextBox.type = 'text';
  29. urlTextBox.id = 'fileUrl';
  30. urlTextBox.placeholder = 'URL will appear here';
  31. urlTextBox.readOnly = true;
  32. urlTextBox.style.display = 'none';
  33. document.body.appendChild(urlTextBox);
  34.  
  35. const copyButton = document.createElement('div');
  36. copyButton.id = 'copyButton';
  37. copyButton.innerHTML = '📋';
  38. copyButton.style.display = 'none';
  39. document.body.appendChild(copyButton);
  40.  
  41. const dropZone = document.createElement('div');
  42. dropZone.id = 'dropZone';
  43. dropZone.innerText = 'Drag & Drop File Here';
  44. dropZone.style.display = 'none';
  45. document.body.appendChild(dropZone);
  46.  
  47. const minimizeButton = document.createElement('div');
  48. minimizeButton.id = 'minimizeButton';
  49. minimizeButton.innerHTML = '—';
  50. minimizeButton.style.display = 'none';
  51. document.body.appendChild(minimizeButton);
  52.  
  53. const historyButton = document.createElement('div');
  54. historyButton.id = 'historyButton';
  55. historyButton.innerHTML = '📜';
  56. historyButton.style.display = 'none';
  57. document.body.appendChild(historyButton);
  58.  
  59. const clearHistoryButton = document.createElement('div');
  60. clearHistoryButton.id = 'clearHistoryButton';
  61. clearHistoryButton.innerHTML = '🗑️';
  62. clearHistoryButton.style.display = 'none';
  63. document.body.appendChild(clearHistoryButton);
  64.  
  65. const historyList = document.createElement('div');
  66. historyList.id = 'historyList';
  67. historyList.style.display = 'none';
  68. document.body.appendChild(historyList);
  69.  
  70. const durationButton = document.createElement('div');
  71. durationButton.id = 'durationButton';
  72. durationButton.innerHTML = GM_getValue('uploadDuration', '1h'); // Display initial duration
  73. durationButton.style.display = 'none';
  74. document.body.appendChild(durationButton);
  75.  
  76. const durationMenu = document.createElement('div');
  77. durationMenu.id = 'durationMenu';
  78. durationMenu.style.display = 'none';
  79. durationMenu.innerHTML = `
  80. <div data-duration="1h">1 hour</div>
  81. <div data-duration="12h">12 hours</div>
  82. <div data-duration="24h">24 hours</div>
  83. <div data-duration="72h">72 hours</div>
  84. `;
  85. document.body.appendChild(durationMenu);
  86.  
  87. GM_addStyle(`
  88. #uploadButton, #historyButton, #clearHistoryButton, #minimizeButton, #durationButton {
  89. position: fixed;
  90. bottom: 20px;
  91. width: 50px;
  92. height: 50px;
  93. background-color: #333;
  94. color: white;
  95. border: none;
  96. border-radius: 50%;
  97. cursor: pointer;
  98. font-family: Arial, sans-serif;
  99. font-size: 24px;
  100. text-align: center;
  101. line-height: 50px;
  102. box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
  103. z-index: 10000;
  104. }
  105. #uploadButton { left: 20px; }
  106. #uploadButton.minimized {
  107. bottom: 0;
  108. width: 50px;
  109. height: 10px;
  110. border-radius: 50px 50px 0 0;
  111. font-size: 10px;
  112. line-height: 10px;
  113. }
  114. #minimizeButton, #historyButton, #clearHistoryButton, #durationButton {
  115. width: 40px;
  116. height: 40px;
  117. font-size: 20px;
  118. line-height: 40px;
  119. }
  120. #minimizeButton { left: 80px; }
  121. #historyButton { left: 140px; }
  122. #clearHistoryButton { left: 200px; }
  123. #durationButton { left: 260px; }
  124.  
  125. #fileUrl, #historyList, #durationMenu {
  126. position: fixed;
  127. bottom: 80px;
  128. left: 20px;
  129. width: 270px;
  130. background-color: #333;
  131. color: white;
  132. padding: 10px;
  133. border: none;
  134. border-radius: 5px;
  135. font-size: 14px;
  136. box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
  137. z-index: 10000;
  138. overflow-y: auto;
  139. }
  140. #fileUrl { display: block; }
  141. #historyList {
  142. height: 200px;
  143. display: none;
  144. }
  145. #historyList div {
  146. margin-bottom: 10px;
  147. }
  148. #historyList div span.timestamp {
  149. display: block;
  150. color: #aaa;
  151. font-size: 12px;
  152. margin-top: 4px;
  153. border-top: 1px solid #555;
  154. padding-top: 2px;
  155. }
  156. #historyList a {
  157. color: #66ccff;
  158. text-decoration: none;
  159. }
  160. #copyButton {
  161. position: fixed;
  162. bottom: 80px;
  163. left: 300px;
  164. width: 30px;
  165. height: 30px;
  166. background-color: #333;
  167. color: white;
  168. border: none;
  169. border-radius: 5px;
  170. cursor: pointer;
  171. font-family: Arial, sans-serif;
  172. font-size: 16px;
  173. text-align: center;
  174. line-height: 30px;
  175. box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
  176. z-index: 10000;
  177. }
  178. #dropZone {
  179. position: fixed;
  180. bottom: 150px;
  181. left: 20px;
  182. width: 300px;
  183. height: 150px;
  184. border: 2px dashed #aaa;
  185. background-color: #444;
  186. color: white;
  187. text-align: center;
  188. line-height: 150px;
  189. font-family: Arial, sans-serif;
  190. font-size: 14px;
  191. border-radius: 5px;
  192. z-index: 10000;
  193. }
  194. #dropZone.dragover {
  195. border-color: #fff;
  196. background-color: #555;
  197. }
  198. #durationMenu div:hover {
  199. background-color: #555;
  200. }
  201. `);
  202.  
  203. let isMinimized = true;
  204. let uploadDuration = GM_getValue('uploadDuration', '1h');
  205. uploadButton.classList.add('minimized');
  206.  
  207. uploadButton.addEventListener('click', () => {
  208. if (isMinimized) {
  209. uploadButton.classList.remove('minimized');
  210. isMinimized = false;
  211. minimizeButton.style.display = 'block';
  212. historyButton.style.display = 'block';
  213. clearHistoryButton.style.display = 'block';
  214. durationButton.style.display = 'block';
  215. } else {
  216. fileInput.click();
  217. }
  218. });
  219.  
  220. minimizeButton.addEventListener('click', () => {
  221. uploadButton.classList.add('minimized');
  222. isMinimized = true;
  223. minimizeButton.style.display = 'none';
  224. urlTextBox.style.display = 'none';
  225. copyButton.style.display = 'none';
  226. historyList.style.display = 'none';
  227. historyButton.style.display = 'none';
  228. clearHistoryButton.style.display = 'none';
  229. durationButton.style.display = 'none';
  230. dropZone.style.display = 'none';
  231. durationMenu.style.display = 'none';
  232. });
  233.  
  234. durationButton.addEventListener('click', () => {
  235. durationMenu.style.display = durationMenu.style.display === 'none' ? 'block' : 'none';
  236. });
  237.  
  238. durationMenu.addEventListener('click', (e) => {
  239. if (e.target.dataset.duration) {
  240. uploadDuration = e.target.dataset.duration;
  241. GM_setValue('uploadDuration', uploadDuration);
  242. durationButton.innerHTML = uploadDuration;
  243. durationMenu.style.display = 'none';
  244. }
  245. });
  246.  
  247. fileInput.addEventListener('change', () => {
  248. const file = fileInput.files[0];
  249. if (file) uploadFile(file);
  250. });
  251.  
  252. const uploadedUrlsKey = 'globalUploadedUrls';
  253. const urlLimit = 10;
  254.  
  255. let savedUrls = GM_getValue(uploadedUrlsKey, []);
  256.  
  257. function updateHistoryList() {
  258. historyList.innerHTML = savedUrls.map(item => `<div><a href="${item.url}" target="_blank">${item.url}</a><span class="timestamp">${item.timestamp}</span></div>`).join('');
  259. }
  260.  
  261. historyButton.addEventListener('click', () => {
  262. historyList.style.display = historyList.style.display === 'none' ? 'block' : 'none';
  263. updateHistoryList();
  264. });
  265.  
  266. clearHistoryButton.addEventListener('click', () => {
  267. if (confirm("Are you sure you want to clear the URL history? This action cannot be undone.")) {
  268. savedUrls = [];
  269. GM_setValue(uploadedUrlsKey, savedUrls);
  270. updateHistoryList();
  271. }
  272. });
  273.  
  274. function handleFileDrop(e) {
  275. e.preventDefault();
  276. dropZone.classList.remove('dragover');
  277. const file = e.dataTransfer.files[0];
  278. if (file) uploadFile(file);
  279. }
  280.  
  281. document.addEventListener('dragover', e => {
  282. e.preventDefault();
  283. dropZone.style.display = 'block';
  284. dropZone.classList.add('dragover');
  285. });
  286.  
  287. document.addEventListener('dragleave', () => dropZone.style.display = 'none');
  288. dropZone.addEventListener('drop', handleFileDrop);
  289.  
  290. async function uploadFile(file) {
  291. const timestamp = new Date().toLocaleString('en-GB');
  292. try {
  293. const formData = new FormData();
  294. formData.append('reqtype', 'fileupload');
  295. formData.append('fileToUpload', file);
  296.  
  297. const response = await fetch('https://litterbox.catbox.moe/resources/internals/api.php', {
  298. method: 'POST',
  299. body: formData
  300. });
  301.  
  302. const url = await response.text();
  303. const newEntry = { url, timestamp };
  304. savedUrls.push(newEntry);
  305. if (savedUrls.length > urlLimit) savedUrls.shift();
  306. GM_setValue(uploadedUrlsKey, savedUrls);
  307.  
  308. urlTextBox.value = url;
  309. urlTextBox.style.display = 'block';
  310. copyButton.style.display = 'block';
  311.  
  312. updateHistoryList();
  313. } catch (error) {
  314. console.error("Upload failed:", error);
  315. }
  316. }
  317.  
  318. copyButton.addEventListener('click', () => {
  319. navigator.clipboard.writeText(urlTextBox.value).then(() => alert('Copied to clipboard!'));
  320. });
  321.  
  322. })();