Lurl Video & Image Downloader (Top Button)

Enables video and image downloads on Lurl.cc, with the download button placed at the top of the page.

  1. // ==UserScript==
  2. // @license MIT
  3. // @name Lurl Video & Image Downloader (Top Button)
  4. // @namespace http://tampermonkey.net/
  5. // @version 1.51
  6. // @description Enables video and image downloads on Lurl.cc, with the download button placed at the top of the page.
  7. // @author You
  8. // @match https://lurl.cc/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Function to create and position the download button at the top
  16. function CreateDownloadButton(videoUrl, fileName) {
  17. // Check if button already exists
  18. if (document.getElementById("lurl-download-button")) return;
  19.  
  20. // Create a fixed top bar container
  21. let topBar = document.createElement("div");
  22. topBar.setAttribute("id", "lurl-top-bar");
  23. topBar.setAttribute("style", `
  24. position: fixed;
  25. top: 0;
  26. left: 0;
  27. width: 100%;
  28. background: #28a745;
  29. padding: 10px;
  30. text-align: center;
  31. z-index: 10000;
  32. font-size: 16px;
  33. font-weight: bold;
  34. `);
  35.  
  36. // Create the download button
  37. let downloadButton = document.createElement("a");
  38. downloadButton.innerText = "📥 下載影片";
  39. downloadButton.setAttribute("id", "lurl-download-button");
  40. downloadButton.setAttribute("href", videoUrl);
  41. downloadButton.setAttribute("download", fileName);
  42. downloadButton.setAttribute("style", `
  43. display: inline-block;
  44. padding: 10px 15px;
  45. background: #fff;
  46. color: #28a745;
  47. text-decoration: none;
  48. border-radius: 5px;
  49. font-size: 14px;
  50. font-weight: bold;
  51. `);
  52.  
  53. // Append button to top bar
  54. topBar.appendChild(downloadButton);
  55. document.body.insertBefore(topBar, document.body.firstChild);
  56. }
  57.  
  58. // Function to handle video download button placement
  59. function DownloadVideo() {
  60. let videoElement = document.querySelector('video');
  61. if (!videoElement) {
  62. console.warn("No video element found.");
  63. return;
  64. }
  65.  
  66. let sourceElement = videoElement.querySelector('source');
  67. if (!sourceElement || !sourceElement.src) {
  68. console.error("No video source found.");
  69. return;
  70. }
  71.  
  72. let videoUrl = sourceElement.src;
  73. let pageTitle = document.title || "video"; // Fallback title
  74.  
  75. // Place the download button at the top of the page
  76. CreateDownloadButton(videoUrl, pageTitle + ".mp4");
  77.  
  78. console.log("✅ Download button added at the top of the page.");
  79. }
  80.  
  81. // Function to solve and download protected images
  82. function PictureSolve() {
  83. const parentElement = document.querySelector("#canvas_div_lurl")?.parentElement;
  84. if (!parentElement) {
  85. console.warn("Parent element not found.");
  86. return;
  87. }
  88.  
  89. const scriptElements = parentElement.querySelectorAll("script");
  90. if (scriptElements.length < 2) {
  91. console.error("Not enough script elements found.");
  92. return;
  93. }
  94.  
  95. const secondScriptElement = scriptElements[1];
  96. const scriptText = secondScriptElement.innerHTML;
  97. const regex = /canvas_img\(['"](https:\/\/[^'"]+)['"]/;
  98. const match = scriptText.match(regex);
  99.  
  100. if (match) {
  101. const imageURL = match[1];
  102.  
  103. // Create a download button for the image
  104. CreateDownloadButton(imageURL, "image.png");
  105.  
  106. console.log("✅ Image download button added at the top.");
  107. } else {
  108. console.error("Image URL not found in script text.");
  109. }
  110. }
  111.  
  112. // Function to auto-click adult verification on Dcard
  113. function SexBoard() {
  114. var buttons = document.querySelectorAll('button');
  115.  
  116. if (buttons.length == 13) {
  117. ClickOK();
  118. }
  119.  
  120. function ClickOK() {
  121. var pElements = document.getElementsByTagName('p');
  122. var nextSiblingElement = pElements[1]?.nextSibling;
  123.  
  124. if (nextSiblingElement?.nodeType === 1) {
  125. var buttons = nextSiblingElement.querySelectorAll('button');
  126.  
  127. if (buttons.length >= 2) {
  128. buttons[1].click();
  129. }
  130. }
  131. }
  132.  
  133. document.querySelectorAll('.__portal').forEach(div => div.remove());
  134. document.body.style.overflow = 'auto';
  135. }
  136.  
  137. // Function to enhance video player experience
  138. function EnhanceVideoPlayer() {
  139. let TureUrl = document.querySelector('source')?.src;
  140. if (!TureUrl) return;
  141.  
  142. let existingVideo = document.querySelector('video');
  143. if (!existingVideo) return;
  144.  
  145. let newVideo = document.createElement('video');
  146. newVideo.src = TureUrl;
  147. newVideo.controls = true;
  148. newVideo.autoplay = true;
  149. newVideo.width = 640;
  150. newVideo.height = 360;
  151. newVideo.preload = 'metadata';
  152. newVideo.classList.add('vjs-tech');
  153. newVideo.setAttribute('data-setup', '{"aspectRatio":"16:9"}');
  154. newVideo.id = 'vjs_video_3_html5_api';
  155. newVideo.tabIndex = -1;
  156. newVideo.setAttribute('role', 'application');
  157.  
  158. existingVideo.parentNode.replaceChild(newVideo, existingVideo);
  159.  
  160. let videoContainer = document.getElementById('vjs_video_3');
  161. videoContainer?.removeAttribute('oncontextmenu');
  162. videoContainer?.removeAttribute('controlslist');
  163.  
  164. document.querySelectorAll('.vjs-control-bar').forEach(controlBar => {
  165. controlBar.parentNode.removeChild(controlBar);
  166. });
  167. }
  168.  
  169. // CSS Loader
  170. function LoadStyles() {
  171. let linkElement = document.createElement('link');
  172. linkElement.setAttribute('rel', 'stylesheet');
  173. linkElement.setAttribute('type', 'text/css');
  174. linkElement.setAttribute('href', 'https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css');
  175.  
  176. let scriptElement = document.createElement('script');
  177. scriptElement.setAttribute('type', 'text/javascript');
  178. scriptElement.setAttribute('src', 'https://cdn.jsdelivr.net/npm/toastify-js');
  179.  
  180. let headElement = document.head || document.getElementsByTagName('head')[0];
  181. headElement.appendChild(linkElement);
  182. headElement.appendChild(scriptElement);
  183. }
  184.  
  185. // Main execution
  186. function Init() {
  187. let currentUrl = window.location.href;
  188.  
  189. if (currentUrl.startsWith('https://www.dcard.tw/f/sex')) {
  190. SexBoard();
  191. setTimeout(SexBoard, 3500);
  192. } else {
  193. LoadStyles();
  194.  
  195. window.addEventListener("load", function() {
  196. setTimeout(() => {
  197. let videoElement = document.querySelector('video');
  198. if (videoElement) {
  199. DownloadVideo();
  200. EnhanceVideoPlayer();
  201. } else {
  202. PictureSolve();
  203. }
  204. }, 2000);
  205. });
  206. }
  207. }
  208.  
  209. Init();
  210. })();