YouTube Cobalt Tools Download Button Lite

Adds a download button to YouTube videos using Cobalt Frontend for downloading videos.

  1. // ==UserScript==
  2. // @name YouTube Cobalt Tools Download Button Lite
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.2
  5. // @description Adds a download button to YouTube videos using Cobalt Frontend for downloading videos.
  6. // @author yodaluca23
  7. // @license GNU GPLv3
  8. // @match *://*.youtube.com/*
  9. // @match *://*cobalt.tools/*
  10. // @run-at document-idle
  11. // @inject-into content
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. let currentPageUrl = window.location.href;
  18. let initialInjectDelay = 2000; // Initial delay in milliseconds
  19. let cobaltInitialInjectDelay = 2000; // Cobalt Initial delay in milliseconds
  20. let navigationInjectDelay = 1000; // Delay on navigation in milliseconds
  21.  
  22. // Check if currentPageUrl is YouTube video
  23. function isCobaltURL() {
  24. return (window.yt==undefined);
  25. }
  26.  
  27. function isYouTubeWatchURL() {
  28. return window.location.href.includes("youtube.com/watch?");
  29. }
  30.  
  31. function removeElement(elementToRemove) {
  32. var element = document.querySelector(elementToRemove);
  33. if (element) {
  34. element.remove();
  35. }
  36. }
  37.  
  38. function waitForLoadingComplete(element) {
  39. return new Promise((resolve) => {
  40. const checkLoadingState = setInterval(() => {
  41. const icon = document.querySelector(element);
  42.  
  43. if (icon && !icon.className.includes("loading")) {
  44. clearInterval(checkLoadingState);
  45. resolve();
  46. }
  47. }, 100);
  48. });
  49. }
  50.  
  51. function waitForSaveDownloadButton() {
  52. return new Promise((resolve) => {
  53. const checkButtonState = setInterval(() => {
  54. const button = document.querySelector("#button-save-download");
  55.  
  56. if (button) {
  57. clearInterval(checkButtonState);
  58. resolve();
  59. }
  60. }, 100);
  61. });
  62. }
  63.  
  64. function cobaltWebsiteSimulation() {
  65. // Function to check if input length is greater than 5
  66. if (window.document.getElementById('link-area').value.length > 5) {
  67. document.getElementById('download-button').click();
  68. waitForSaveDownloadButton().then(() => {
  69. document.querySelector("#button-save-download").click()
  70. // close the cobalt tab
  71. setTimeout(() => {
  72. window.close();
  73. }, 1000);
  74. });
  75. }
  76. }
  77.  
  78. // Helper function to check if two arrays are equal (for detecting changes)
  79. function arraysEqual(arr1, arr2) {
  80. if (arr1.length !== arr2.length) return false;
  81. for (let i = 0; i < arr1.length; i++) {
  82. if (arr1[i] !== arr2[i]) return false;
  83. }
  84. return true;
  85. }
  86.  
  87. // Function to inject download button on the page
  88. function injectDownloadButton() {
  89. setTimeout(() => {
  90. // Remove existing download button if present
  91. removeElement('#cobalt-download-btn');
  92.  
  93. const downloadButton = document.createElement('button');
  94. downloadButton.id = 'cobalt-download-btn';
  95. downloadButton.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-leading';
  96. downloadButton.setAttribute('aria-label', 'Download');
  97. downloadButton.setAttribute('title', 'Download');
  98. downloadButton.innerHTML = `
  99. <div class="yt-spec-button-shape-next__icon">
  100. <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" style="pointer-events: none; display: inline-block; width: 24px; height: 24px; vertical-align: middle;">
  101. <path fill="currentColor" d="M17 18v1H6v-1h11zm-.5-6.6-.7-.7-3.8 3.7V4h-1v10.4l-3.8-3.8-.7.7 5 5 5-4.9z"></path>
  102. </svg>
  103. </div>
  104. <div class="yt-spec-button-shape-next__button-text-content">Download</div>
  105. `;
  106. downloadButton.style.borderRadius = '30px';
  107. downloadButton.style.fontSize = '14px';
  108. downloadButton.style.padding = '8px 16px';
  109. downloadButton.style.cursor = 'pointer';
  110. downloadButton.style.marginLeft = '8px';
  111. downloadButton.style.marginRight = '0px';
  112.  
  113. downloadButton.onclick = () => window.open("https://cobalt.tools/#" + window.location.href, '_blank', 'noopener,noreferrer');
  114.  
  115. const actionMenu = document.querySelector('.top-level-buttons');
  116. actionMenu.appendChild(downloadButton);
  117. }, initialInjectDelay);
  118. }
  119.  
  120. // Function to remove native YouTube download button
  121. function removeNativeDownloadButton() {
  122. setTimeout(() => {
  123. // Remove download button from overflow menu
  124. removeElement('ytd-menu-service-item-download-renderer');
  125.  
  126. // Remove download button next to like/dislike buttons
  127. var overFlowButton = document.querySelector('button[aria-label="More actions"]');
  128. overFlowButton.click();
  129. removeElement('ytd-download-button-renderer');
  130. overFlowButton.click();
  131. }, initialInjectDelay);
  132. }
  133.  
  134. // Function to initialize download button on YouTube video page
  135. function initializeDownloadButton() {
  136. injectDownloadButton();
  137. removeNativeDownloadButton();
  138. }
  139.  
  140. // Initialize on page load
  141. if (isYouTubeWatchURL()) {
  142. setTimeout(() => {
  143. initializeDownloadButton();
  144. }, initialInjectDelay);
  145. }
  146.  
  147. if (isCobaltURL()) {
  148. setTimeout(() => {
  149. waitForLoadingComplete("#input-icons").then(() => {
  150. cobaltWebsiteSimulation();
  151. });
  152. }, cobaltInitialInjectDelay);
  153. }
  154.  
  155. // Monitor URL changes using history API
  156. window.onpopstate = function(event) {
  157. setTimeout(() => {
  158. if (currentPageUrl !== window.location.href) {
  159. currentPageUrl = window.location.href;
  160. console.log('URL changed:', currentPageUrl);
  161. if (isYouTubeWatchURL()) {
  162. initializeDownloadButton(); // Reinitialize download button on URL change
  163. }
  164.  
  165. // Close the format/quality picker menu if a new video is clicked
  166. removeElement('#cobalt-quality-picker');
  167. }
  168. }, navigationInjectDelay);
  169. };
  170.  
  171. // Monitor DOM changes using MutationObserver
  172. const observer = new MutationObserver(mutations => {
  173. for (let mutation of mutations) {
  174. if (mutation.type === 'childList' && mutation.target.classList.contains('html5-video-player')) {
  175. console.log('Video player changed');
  176. setTimeout(() => {
  177. currentPageUrl = window.location.href;
  178. if (isYouTubeWatchURL()) {
  179. initializeDownloadButton(); // Reinitialize download button if video player changes
  180. }
  181. }, navigationInjectDelay);
  182.  
  183. // Close the format/quality picker menu if a new video is clicked
  184. removeElement('#cobalt-quality-picker');
  185. break;
  186. }
  187. }
  188. });
  189.  
  190. observer.observe(document.body, {
  191. childList: true,
  192. subtree: true,
  193. });
  194.  
  195. })();