YouTube Quick Watch Later

Adds quick Watch Later button

  1. // ==UserScript==
  2. // @name YouTube Quick Watch Later
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.4
  5. // @description Adds quick Watch Later button
  6. // @author kavinned
  7. // @match https://www.youtube.com/*
  8. // @grant GM_addStyle
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=YouTube.com
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. // Configuration: Add "Save" translations here
  17. const SAVE_BUTTON_TEXTS = [
  18. "Save", // English
  19. "儲存", // Traditional Chinese
  20. "保存", // Simplified Chinese
  21. "Guardar", // Spanish
  22. "Sauvegarder", // French
  23. "Speichern", // German
  24. "Salvar", // Portuguese
  25. "Сохранить", // Russian
  26. "保存", // Japanese
  27. "저장", // Korean
  28. "บันทึก", // Thai
  29. "Simpan", // Indonesian
  30. "Lưu" // Vietnamese
  31. ];
  32.  
  33. function addWatchLaterButton() {
  34. const targetDiv = document.querySelector(
  35. "#top-level-buttons-computed > segmented-like-dislike-button-view-model > yt-smartimation > div"
  36. );
  37. if (!targetDiv || document.querySelector(".quick-watch-later")) return;
  38.  
  39. const button = document.createElement("button");
  40. button.className = "quick-watch-later";
  41. button.textContent = "WL";
  42.  
  43. button.addEventListener("click", function () {
  44. // Helper function to check if text contains any of the save button texts
  45. function containsSaveText(text) {
  46. return SAVE_BUTTON_TEXTS.some(saveText => text.includes(saveText));
  47. }
  48.  
  49. // First check if Save button is directly visible
  50. const directSaveButton = document.querySelector(
  51. "ytd-menu-renderer yt-button-view-model .yt-spec-button-shape-next__button-text-content"
  52. );
  53. const directSaveButtonWithText = Array.from(
  54. document.querySelectorAll("ytd-menu-renderer yt-button-view-model .yt-spec-button-shape-next__button-text-content")
  55. ).find(element => containsSaveText(element.textContent));
  56.  
  57. if (directSaveButtonWithText) {
  58. // Save button is directly visible, click it
  59. console.log("Direct save button found, clicking it");
  60. directSaveButtonWithText.click();
  61. // Then proceed to click the Watch Later button
  62. setTimeout(function () {
  63. const watchLaterBox = document.querySelector(
  64. "#playlists > ytd-playlist-add-to-option-renderer:first-child #checkbox"
  65. );
  66.  
  67. if (
  68. watchLaterBox &&
  69. watchLaterBox.getAttribute("aria-checked") === "true"
  70. ) {
  71. const confirmRemove = window.confirm(
  72. "This video is already in your Watch Later playlist. Do you want to remove it?"
  73. );
  74.  
  75. if (confirmRemove) {
  76. watchLaterBox.click();
  77. const closeButton = document.querySelector(
  78. "#button > yt-icon > span"
  79. );
  80. if (closeButton) {
  81. closeButton.click();
  82. }
  83. } else {
  84. const closeButton = document.querySelector(
  85. "#button > yt-icon > span"
  86. );
  87. if (closeButton) {
  88. closeButton.click();
  89. }
  90. }
  91. } else {
  92. // If the video isn't in Watch Later, proceed with adding it
  93. watchLaterBox.click();
  94. const closeButton = document.querySelector(
  95. "#button > yt-icon > span"
  96. );
  97. if (closeButton) {
  98. closeButton.click();
  99. }
  100. }
  101. }, 1000);
  102. } else {
  103. // Save button is in submenu, use original logic
  104. console.log("Save button not directly visible, opening menu");
  105. // First click menu button
  106. const menuButton = document.querySelector(
  107. "#button-shape > button > yt-touch-feedback-shape > div"
  108. );
  109. menuButton.click();
  110. if (menuButton) {
  111. console.log("menu clicked");
  112. }
  113. // Next click the Save button
  114. setTimeout(function () {
  115. const saveButtons = document.querySelectorAll(
  116. "#items > ytd-menu-service-item-renderer > tp-yt-paper-item > yt-formatted-string"
  117. );
  118. const saveButton = Array.from(saveButtons).find((button) =>
  119. containsSaveText(button.textContent)
  120. );
  121. saveButton.click();
  122. // Then click the Watch Later button
  123. setTimeout(function () {
  124. const watchLaterBox = document.querySelector(
  125. "#playlists > ytd-playlist-add-to-option-renderer:first-child #checkbox"
  126. );
  127.  
  128. if (
  129. watchLaterBox &&
  130. watchLaterBox.getAttribute("aria-checked") === "true"
  131. ) {
  132. const confirmRemove = window.confirm(
  133. "This video is already in your Watch Later playlist. Do you want to remove it?"
  134. );
  135.  
  136. if (confirmRemove) {
  137. watchLaterBox.click();
  138. const closeButton = document.querySelector(
  139. "#button > yt-icon > span"
  140. );
  141. if (closeButton) {
  142. closeButton.click();
  143. }
  144. } else {
  145. const closeButton = document.querySelector(
  146. "#button > yt-icon > span"
  147. );
  148. if (closeButton) {
  149. closeButton.click();
  150. }
  151. }
  152. } else {
  153. // If the video isn't in Watch Later, proceed with adding it
  154. watchLaterBox.click();
  155. const closeButton = document.querySelector(
  156. "#button > yt-icon > span"
  157. );
  158. if (closeButton) {
  159. closeButton.click();
  160. }
  161. }
  162. }, 1000);
  163. }, 100);
  164. }
  165. });
  166.  
  167. targetDiv.appendChild(button);
  168. }
  169.  
  170. setTimeout(addWatchLaterButton, 2000);
  171.  
  172. const observer = new MutationObserver(() => {
  173. if (window.location.href.includes("/watch?")) {
  174. setTimeout(addWatchLaterButton, 1000);
  175. }
  176. });
  177.  
  178. observer.observe(document.body, { childList: true, subtree: true });
  179.  
  180. GM_addStyle(
  181. `#top-level-buttons-computed > segmented-like-dislike-button-view-model > yt-smartimation > div {
  182. display: flex;
  183. flex-direction: row-reverse;
  184. gap: 5px;
  185. }
  186. #top-level-buttons-computed > segmented-like-dislike-button-view-model > yt-smartimation > div > button {
  187. flex-direction: row;
  188. border-radius: 24px;
  189. border: none;
  190. padding-left: 20px;
  191. padding-right: 20px;
  192. color: white;
  193. font-weight: bold;
  194. background: #272727;
  195. cursor: pointer;
  196.  
  197. &:hover {
  198. background: #414141;
  199. }
  200. }
  201. .ryd-tooltip.ryd-tooltip-new-design {
  202. height: 0px !important;
  203. width: 0px !important;
  204. }
  205. @media only screen and (max-width: 1200px) {
  206. #top-level-buttons-computed > segmented-like-dislike-button-view-model > yt-smartimation > div {
  207. flex-direction: column;
  208. }
  209. #top-level-buttons-computed > segmented-like-dislike-button-view-model > yt-smartimation > div > button {
  210. padding: 10.5px 0px;
  211. }
  212.  
  213. }
  214. }`
  215. );
  216. })();