YouTube Embed Switcher & Modal Controls

Switch between /watch, /shorts and /embed + modal window with settings

  1. // ==UserScript==
  2. // @name YouTube Embed Switcher & Modal Controls
  3. // @namespace GPT
  4. // @version 1.0.9
  5. // @description Switch between /watch, /shorts and /embed + modal window with settings
  6. // @description:ru Переключение между /watch, /shorts и /embed + модальное окно с настройками
  7. // @author Wizzergod
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  9. // @match *://www.youtube.com/*
  10. // @match *://youtube.com/*
  11. // @match *://youtu.be/*
  12. // @grant GM_addStyle
  13. // @grant GM_getResourceURL
  14. // @grant GM_openInTab
  15. // @grant GM_xmlhttpRequest
  16. // @grant unsafeWindow
  17. // @resource icon https://images.icon-icons.com/3653/PNG/512/filter_settings_filters_icon_228291.png
  18. // @run-at document-body
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. (function () {
  23. 'use strict';
  24.  
  25. const iconURL = GM_getResourceURL('icon');
  26.  
  27. let settings = {
  28. redirectShorts: true,
  29. redirectWatch: false
  30. };
  31.  
  32. const STORAGE_KEY = 'yt_embed_settings';
  33.  
  34. function loadSettings() {
  35. try {
  36. const stored = localStorage.getItem(STORAGE_KEY);
  37. if (stored) Object.assign(settings, JSON.parse(stored));
  38. } catch (e) {}
  39. }
  40.  
  41. function saveSettings() {
  42. localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
  43. }
  44.  
  45. function getVideoId(url) {
  46. const m = url.match(/[?&]v=([^&]+)/) || url.match(/shorts\/([\w-]+)/) || url.match(/embed\/([\w-]+)/);
  47. return m ? m[1] : null;
  48. }
  49.  
  50. function redirectIfNeeded() {
  51. const url = window.location.href;
  52.  
  53. if (settings.redirectShorts && url.includes('/shorts/')) {
  54. const id = getVideoId(url);
  55. if (id) location.href = `https://www.youtube.com/embed/${id}`;
  56. }
  57.  
  58. if (settings.redirectWatch && url.includes('/watch?v=')) {
  59. const id = getVideoId(url);
  60. if (id) location.href = `https://www.youtube.com/embed/${id}`;
  61. }
  62. }
  63.  
  64. function createOverlay() {
  65. const overlay = document.createElement('div');
  66. overlay.id = 'yt-embed-modal';
  67.  
  68. const modal = document.createElement('div');
  69. modal.className = 'modal-content';
  70.  
  71. const header = document.createElement('h2');
  72. header.textContent = 'Redirect and Settings';
  73. modal.appendChild(header);
  74.  
  75. const actions = [
  76. ['goto-watch', '/watch'],
  77. ['goto-shorts', '/shorts'],
  78. ['goto-embed', '/embed']
  79. ];
  80.  
  81. actions.forEach(([action, label]) => {
  82. const btn = document.createElement('button');
  83. btn.textContent = `Go ${label}`;
  84. btn.dataset.action = action;
  85. modal.appendChild(btn);
  86. });
  87.  
  88. modal.appendChild(document.createElement('hr'));
  89.  
  90. const shortsLabel = document.createElement('label');
  91. const shortsCheckbox = document.createElement('input');
  92. shortsCheckbox.type = 'checkbox';
  93. shortsCheckbox.id = 'toggle-shorts';
  94. shortsLabel.appendChild(shortsCheckbox);
  95. shortsLabel.append('Auto /shorts ᐉ /embed');
  96. modal.appendChild(shortsLabel);
  97. modal.appendChild(document.createElement('br'));
  98.  
  99. const watchLabel = document.createElement('label');
  100. const watchCheckbox = document.createElement('input');
  101. watchCheckbox.type = 'checkbox';
  102. watchCheckbox.id = 'toggle-watch';
  103. watchLabel.appendChild(watchCheckbox);
  104. watchLabel.append('Auto /watch ᐉ /embed');
  105. modal.appendChild(watchLabel);
  106. modal.appendChild(document.createElement('br'));
  107.  
  108. modal.appendChild(document.createElement('br'));
  109.  
  110.  
  111.  
  112.  
  113. const closeBtn = document.createElement('button');
  114. closeBtn.id = 'close-modal';
  115. closeBtn.textContent = '❌ Close';
  116. modal.appendChild(closeBtn);
  117.  
  118. overlay.appendChild(modal);
  119. document.body.appendChild(overlay);
  120.  
  121. shortsCheckbox.checked = settings.redirectShorts;
  122. watchCheckbox.checked = settings.redirectWatch;
  123.  
  124. shortsCheckbox.addEventListener('change', (e) => {
  125. settings.redirectShorts = e.target.checked;
  126. saveSettings();
  127. });
  128.  
  129. watchCheckbox.addEventListener('change', (e) => {
  130. settings.redirectWatch = e.target.checked;
  131. saveSettings();
  132. });
  133.  
  134. modal.querySelectorAll('button[data-action]').forEach(btn => {
  135. btn.addEventListener('click', () => {
  136. const id = getVideoId(window.location.href);
  137. if (!id) return;
  138. const action = btn.dataset.action;
  139. if (action === 'goto-watch') location.href = `https://www.youtube.com/watch?v=${id}`;
  140. if (action === 'goto-shorts') location.href = `https://www.youtube.com/shorts/${id}`;
  141. if (action === 'goto-embed') location.href = `https://www.youtube.com/embed/${id}`;
  142. });
  143. });
  144.  
  145. closeBtn.addEventListener('click', () => {
  146. overlay.style.display = 'none';
  147. });
  148. }
  149.  
  150. function toggleOverlay() {
  151. const modal = document.getElementById('yt-embed-modal');
  152. if (modal) {
  153. modal.style.display = modal.style.display === 'flex' ? 'none' : 'flex';
  154. }
  155. }
  156.  
  157. function addPlayerButton() {
  158. const interval = setInterval(() => {
  159. const controls = document.querySelector('.ytp-right-controls');
  160. if (controls && !document.getElementById('embedSwitchBtn')) {
  161. const btn = document.createElement('button');
  162. btn.id = 'embedSwitchBtn';
  163. btn.className = 'ytp-button';
  164. btn.title = 'Open Settings';
  165. btn.style.backgroundImage = `url(${iconURL})`;
  166. btn.onclick = toggleOverlay;
  167. controls.prepend(btn);
  168. clearInterval(interval);
  169. }
  170. }, 1000);
  171. }
  172.  
  173. GM_addStyle(`
  174. #yt-embed-modal {
  175. display: none;
  176. position: fixed;
  177. z-index: 99999;
  178. top: 0;
  179. left: 0;
  180. width: 100vw;
  181. height: 100vh;
  182. background: #111111c4;
  183. align-items: center;
  184. justify-content: center;
  185. }
  186. .modal-content {
  187. background: #111111c4;
  188. color: #fff;
  189. padding: 20px;
  190. border-radius: 12px;
  191. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45), -3px -3px rgba(0, 0, 0, .1) inset;
  192. min-width: 300px;
  193. text-align: center;
  194. z-index: 99999;
  195. }
  196. .modal-content h2 {
  197. margin-bottom: 10px;
  198. }
  199. .modal-content button {
  200. padding: 5px 8px;
  201. background-color: #ffffffab;
  202. border: 0.1px solid rgba(255, 255, 255, 0.74);
  203. cursor: pointer;
  204. border-radius: 5px;
  205. ition: color 0.3s ease, background-color 0.3s ease;
  206. text-align: center;
  207. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45), -3px -3px rgba(0, 0, 0, .1) inset;
  208. line-height: 1.5rem;
  209. margin: 10px;
  210. form: scale(1.15) !important;
  211. font-family: Helvetica, Arial, "Lucida Grande", sans-serif;
  212. font-size: 16px;
  213. font-weight: bold;
  214. align-items: center;
  215. letter-spacing: -0.04em;
  216. text-decoration: none;
  217. }
  218. .modal-content button:hover {
  219. background-color: #a0bae37d;
  220. border: 0.1px solid #27cde86b;
  221. color: #75ecfff0;
  222. box-shadow: 0 2px 10px #27cde86b, -3px -3px rgba(0, 0, 0, .1) inset;
  223. }
  224. .modal-content button:active {
  225. border: 1px solid #565656a3;
  226. form: late(1px, 1px);
  227. form: scale(1.10) !important;
  228. }
  229. .modal-content input[type="checkbox"] {
  230. form: scale(1.15);
  231. margin-right: 6px;
  232. }
  233. #embedSwitchBtn {
  234. background-size: 30px 30px !important;
  235. background-repeat: no-repeat !important;
  236. background-position: center !important;
  237. /* width: 48px !important; */
  238. /* height: 45px !important; */
  239. display: inline-flex !important;
  240. align-items: center !important;
  241. justify-content: center !important;
  242. cursor: pointer !important;
  243. margin: 0 1px !important;
  244. ition: form 0.3s ease, color 0.3s ease, background-color 0.3s ease !important;
  245. }
  246. #embedSwitchBtn:hover {
  247. form: scale(1.15) !important;
  248. }
  249. #embedSwitchBtn:active {
  250. form: scale(0.90) !important;
  251. background-color: rgba(0, 0, 0, 0.1) !important;
  252. }
  253. #yt-embed-modal .modal-content {
  254. animation: fadeInUp 0.3s ease;
  255. }
  256. @keyframes fadeInUp {
  257. from { form: lateY(20px); opacity: 0; }
  258. to { form: lateY(0); opacity: 1; }
  259. }
  260. `);
  261.  
  262. loadSettings();
  263. createOverlay();
  264. redirectIfNeeded();
  265. addPlayerButton();
  266. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址