Automate YouTube tasks with powerful tools for video management, playback control, and productivity features
Size
20.3 KB
Version
1.0.1
Created
Jan 27, 2026
Updated
21 days ago
1// ==UserScript==
2// @name YouTube Automation Toolkit
3// @description Automate YouTube tasks with powerful tools for video management, playback control, and productivity features
4// @version 1.0.1
5// @match https://*.youtube.com/*
6// @icon https://www.youtube.com/s/desktop/1bb3b4db/img/favicon_32x32.png
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('YouTube Automation Toolkit initialized');
12
13 // Utility function to debounce
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Utility function to wait for element
27 function waitForElement(selector, timeout = 10000) {
28 return new Promise((resolve, reject) => {
29 if (document.querySelector(selector)) {
30 return resolve(document.querySelector(selector));
31 }
32
33 const observer = new MutationObserver(debounce(() => {
34 if (document.querySelector(selector)) {
35 observer.disconnect();
36 resolve(document.querySelector(selector));
37 }
38 }, 100));
39
40 observer.observe(document.body, {
41 childList: true,
42 subtree: true
43 });
44
45 setTimeout(() => {
46 observer.disconnect();
47 reject(new Error(`Element ${selector} not found within ${timeout}ms`));
48 }, timeout);
49 });
50 }
51
52 // Create floating control panel
53 function createControlPanel() {
54 const panel = document.createElement('div');
55 panel.id = 'yt-automation-panel';
56 panel.innerHTML = `
57 <div id="yt-automation-header">
58 <span>🤖 YouTube Toolkit</span>
59 <button id="yt-automation-toggle">−</button>
60 </div>
61 <div id="yt-automation-content">
62 <div class="yt-automation-section">
63 <h3>⚡ Quick Actions</h3>
64 <button class="yt-automation-btn" id="yt-skip-ads">Skip All Ads</button>
65 <button class="yt-automation-btn" id="yt-auto-quality">Auto Max Quality</button>
66 <button class="yt-automation-btn" id="yt-theater-mode">Theater Mode</button>
67 </div>
68 <div class="yt-automation-section">
69 <h3>🎬 Playback Control</h3>
70 <button class="yt-automation-btn" id="yt-speed-up">Speed: <span id="yt-current-speed">1.0</span>x</button>
71 <button class="yt-automation-btn" id="yt-loop-video">Loop: <span id="yt-loop-status">OFF</span></button>
72 <button class="yt-automation-btn" id="yt-screenshot">📸 Screenshot</button>
73 </div>
74 <div class="yt-automation-section">
75 <h3>📋 Playlist Tools</h3>
76 <button class="yt-automation-btn" id="yt-shuffle-playlist">🔀 Shuffle</button>
77 <button class="yt-automation-btn" id="yt-export-playlist">💾 Export List</button>
78 </div>
79 <div class="yt-automation-section">
80 <h3>🎯 Auto Features</h3>
81 <label class="yt-automation-checkbox">
82 <input type="checkbox" id="yt-auto-skip-ads"> Auto-skip ads
83 </label>
84 <label class="yt-automation-checkbox">
85 <input type="checkbox" id="yt-auto-pause-end"> Pause at end
86 </label>
87 <label class="yt-automation-checkbox">
88 <input type="checkbox" id="yt-remove-shorts"> Hide Shorts
89 </label>
90 </div>
91 </div>
92 `;
93
94 document.body.appendChild(panel);
95 addStyles();
96 attachEventListeners();
97 loadSettings();
98 }
99
100 // Add CSS styles
101 function addStyles() {
102 const styles = `
103 #yt-automation-panel {
104 position: fixed;
105 top: 80px;
106 right: 20px;
107 width: 280px;
108 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
109 border-radius: 12px;
110 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
111 z-index: 9999;
112 font-family: 'Roboto', Arial, sans-serif;
113 color: white;
114 overflow: hidden;
115 transition: all 0.3s ease;
116 }
117
118 #yt-automation-panel.collapsed #yt-automation-content {
119 display: none;
120 }
121
122 #yt-automation-header {
123 display: flex;
124 justify-content: space-between;
125 align-items: center;
126 padding: 12px 16px;
127 background: rgba(0, 0, 0, 0.2);
128 cursor: move;
129 font-weight: bold;
130 font-size: 14px;
131 }
132
133 #yt-automation-toggle {
134 background: rgba(255, 255, 255, 0.2);
135 border: none;
136 color: white;
137 width: 24px;
138 height: 24px;
139 border-radius: 4px;
140 cursor: pointer;
141 font-size: 16px;
142 display: flex;
143 align-items: center;
144 justify-content: center;
145 transition: background 0.2s;
146 }
147
148 #yt-automation-toggle:hover {
149 background: rgba(255, 255, 255, 0.3);
150 }
151
152 #yt-automation-content {
153 padding: 12px;
154 max-height: 500px;
155 overflow-y: auto;
156 }
157
158 .yt-automation-section {
159 margin-bottom: 16px;
160 padding-bottom: 12px;
161 border-bottom: 1px solid rgba(255, 255, 255, 0.2);
162 }
163
164 .yt-automation-section:last-child {
165 border-bottom: none;
166 margin-bottom: 0;
167 }
168
169 .yt-automation-section h3 {
170 margin: 0 0 8px 0;
171 font-size: 12px;
172 opacity: 0.9;
173 text-transform: uppercase;
174 letter-spacing: 0.5px;
175 }
176
177 .yt-automation-btn {
178 width: 100%;
179 padding: 8px 12px;
180 margin: 4px 0;
181 background: rgba(255, 255, 255, 0.15);
182 border: 1px solid rgba(255, 255, 255, 0.3);
183 border-radius: 6px;
184 color: white;
185 cursor: pointer;
186 font-size: 13px;
187 transition: all 0.2s;
188 font-weight: 500;
189 }
190
191 .yt-automation-btn:hover {
192 background: rgba(255, 255, 255, 0.25);
193 transform: translateY(-1px);
194 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
195 }
196
197 .yt-automation-btn:active {
198 transform: translateY(0);
199 }
200
201 .yt-automation-checkbox {
202 display: flex;
203 align-items: center;
204 padding: 6px 0;
205 font-size: 13px;
206 cursor: pointer;
207 user-select: none;
208 }
209
210 .yt-automation-checkbox input {
211 margin-right: 8px;
212 cursor: pointer;
213 width: 16px;
214 height: 16px;
215 }
216
217 #yt-automation-content::-webkit-scrollbar {
218 width: 6px;
219 }
220
221 #yt-automation-content::-webkit-scrollbar-track {
222 background: rgba(255, 255, 255, 0.1);
223 border-radius: 3px;
224 }
225
226 #yt-automation-content::-webkit-scrollbar-thumb {
227 background: rgba(255, 255, 255, 0.3);
228 border-radius: 3px;
229 }
230
231 #yt-automation-content::-webkit-scrollbar-thumb:hover {
232 background: rgba(255, 255, 255, 0.4);
233 }
234
235 .yt-notification {
236 position: fixed;
237 top: 20px;
238 right: 20px;
239 background: #4CAF50;
240 color: white;
241 padding: 12px 20px;
242 border-radius: 8px;
243 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
244 z-index: 10000;
245 animation: slideIn 0.3s ease;
246 font-size: 14px;
247 }
248
249 @keyframes slideIn {
250 from {
251 transform: translateX(400px);
252 opacity: 0;
253 }
254 to {
255 transform: translateX(0);
256 opacity: 1;
257 }
258 }
259 `;
260
261 const styleSheet = document.createElement('style');
262 styleSheet.textContent = styles;
263 document.head.appendChild(styleSheet);
264 }
265
266 // Show notification
267 function showNotification(message, duration = 2000) {
268 const notification = document.createElement('div');
269 notification.className = 'yt-notification';
270 notification.textContent = message;
271 document.body.appendChild(notification);
272
273 setTimeout(() => {
274 notification.style.animation = 'slideIn 0.3s ease reverse';
275 setTimeout(() => notification.remove(), 300);
276 }, duration);
277 }
278
279 // Get video element
280 function getVideoElement() {
281 return document.querySelector('video.html5-main-video');
282 }
283
284 // Attach event listeners
285 function attachEventListeners() {
286 // Toggle panel
287 document.getElementById('yt-automation-toggle').addEventListener('click', () => {
288 const panel = document.getElementById('yt-automation-panel');
289 panel.classList.toggle('collapsed');
290 document.getElementById('yt-automation-toggle').textContent =
291 panel.classList.contains('collapsed') ? '+' : '−';
292 });
293
294 // Skip ads
295 document.getElementById('yt-skip-ads').addEventListener('click', skipAds);
296
297 // Auto max quality
298 document.getElementById('yt-auto-quality').addEventListener('click', setMaxQuality);
299
300 // Theater mode
301 document.getElementById('yt-theater-mode').addEventListener('click', toggleTheaterMode);
302
303 // Speed control
304 document.getElementById('yt-speed-up').addEventListener('click', cycleSpeed);
305
306 // Loop video
307 document.getElementById('yt-loop-video').addEventListener('click', toggleLoop);
308
309 // Screenshot
310 document.getElementById('yt-screenshot').addEventListener('click', takeScreenshot);
311
312 // Shuffle playlist
313 document.getElementById('yt-shuffle-playlist').addEventListener('click', shufflePlaylist);
314
315 // Export playlist
316 document.getElementById('yt-export-playlist').addEventListener('click', exportPlaylist);
317
318 // Auto-skip ads checkbox
319 document.getElementById('yt-auto-skip-ads').addEventListener('change', async (e) => {
320 await GM.setValue('autoSkipAds', e.target.checked);
321 showNotification(e.target.checked ? 'Auto-skip ads enabled' : 'Auto-skip ads disabled');
322 if (e.target.checked) {
323 startAdMonitoring();
324 }
325 });
326
327 // Auto-pause at end checkbox
328 document.getElementById('yt-auto-pause-end').addEventListener('change', async (e) => {
329 await GM.setValue('autoPauseEnd', e.target.checked);
330 showNotification(e.target.checked ? 'Auto-pause enabled' : 'Auto-pause disabled');
331 });
332
333 // Remove shorts checkbox
334 document.getElementById('yt-remove-shorts').addEventListener('change', async (e) => {
335 await GM.setValue('removeShorts', e.target.checked);
336 showNotification(e.target.checked ? 'Shorts hidden' : 'Shorts visible');
337 if (e.target.checked) {
338 hideShorts();
339 } else {
340 showShorts();
341 }
342 });
343
344 // Make panel draggable
345 makeDraggable();
346 }
347
348 // Load saved settings
349 async function loadSettings() {
350 const autoSkipAds = await GM.getValue('autoSkipAds', false);
351 const autoPauseEnd = await GM.getValue('autoPauseEnd', false);
352 const removeShorts = await GM.getValue('removeShorts', false);
353
354 document.getElementById('yt-auto-skip-ads').checked = autoSkipAds;
355 document.getElementById('yt-auto-pause-end').checked = autoPauseEnd;
356 document.getElementById('yt-remove-shorts').checked = removeShorts;
357
358 if (autoSkipAds) {
359 startAdMonitoring();
360 }
361
362 if (removeShorts) {
363 hideShorts();
364 }
365 }
366
367 // Skip ads function
368 function skipAds() {
369 const skipButton = document.querySelector('.ytp-ad-skip-button, .ytp-skip-ad-button');
370 if (skipButton) {
371 skipButton.click();
372 showNotification('Ad skipped!');
373 } else {
374 showNotification('No skippable ad found');
375 }
376 }
377
378 // Start ad monitoring
379 function startAdMonitoring() {
380 setInterval(() => {
381 const skipButton = document.querySelector('.ytp-ad-skip-button, .ytp-skip-ad-button');
382 if (skipButton && skipButton.offsetParent !== null) {
383 skipButton.click();
384 console.log('Auto-skipped ad');
385 }
386 }, 1000);
387 }
388
389 // Set max quality
390 async function setMaxQuality() {
391 try {
392 const settingsButton = document.querySelector('button.ytp-settings-button');
393 if (!settingsButton) {
394 showNotification('Settings button not found');
395 return;
396 }
397
398 settingsButton.click();
399 await new Promise(resolve => setTimeout(resolve, 300));
400
401 const qualityMenu = document.querySelector('.ytp-panel-menu .ytp-menuitem:has(.ytp-menuitem-label:contains("Quality"))');
402 if (qualityMenu) {
403 qualityMenu.click();
404 await new Promise(resolve => setTimeout(resolve, 300));
405
406 const qualityOptions = document.querySelectorAll('.ytp-quality-menu .ytp-menuitem');
407 if (qualityOptions.length > 0) {
408 qualityOptions[0].click();
409 showNotification('Quality set to maximum');
410 }
411 }
412
413 settingsButton.click();
414 } catch (error) {
415 console.error('Error setting quality:', error);
416 showNotification('Could not set quality');
417 }
418 }
419
420 // Toggle theater mode
421 function toggleTheaterMode() {
422 const theaterButton = document.querySelector('button.ytp-size-button');
423 if (theaterButton) {
424 theaterButton.click();
425 showNotification('Theater mode toggled');
426 } else {
427 showNotification('Theater button not found');
428 }
429 }
430
431 // Cycle playback speed
432 function cycleSpeed() {
433 const video = getVideoElement();
434 if (!video) {
435 showNotification('Video not found');
436 return;
437 }
438
439 const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
440 const currentSpeed = video.playbackRate;
441 const currentIndex = speeds.indexOf(currentSpeed);
442 const nextIndex = (currentIndex + 1) % speeds.length;
443 const newSpeed = speeds[nextIndex];
444
445 video.playbackRate = newSpeed;
446 document.getElementById('yt-current-speed').textContent = newSpeed.toFixed(2);
447 showNotification(`Speed: ${newSpeed}x`);
448 }
449
450 // Toggle loop
451 function toggleLoop() {
452 const video = getVideoElement();
453 if (!video) {
454 showNotification('Video not found');
455 return;
456 }
457
458 video.loop = !video.loop;
459 const status = video.loop ? 'ON' : 'OFF';
460 document.getElementById('yt-loop-status').textContent = status;
461 showNotification(`Loop: ${status}`);
462 }
463
464 // Take screenshot
465 function takeScreenshot() {
466 const video = getVideoElement();
467 if (!video) {
468 showNotification('Video not found');
469 return;
470 }
471
472 const canvas = document.createElement('canvas');
473 canvas.width = video.videoWidth;
474 canvas.height = video.videoHeight;
475 const ctx = canvas.getContext('2d');
476 ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
477
478 canvas.toBlob((blob) => {
479 const url = URL.createObjectURL(blob);
480 const a = document.createElement('a');
481 a.href = url;
482 a.download = `youtube-screenshot-${Date.now()}.png`;
483 a.click();
484 URL.revokeObjectURL(url);
485 showNotification('Screenshot saved!');
486 });
487 }
488
489 // Shuffle playlist
490 function shufflePlaylist() {
491 const shuffleButton = document.querySelector('button[aria-label*="Shuffle"]');
492 if (shuffleButton) {
493 shuffleButton.click();
494 showNotification('Playlist shuffled');
495 } else {
496 showNotification('Shuffle button not found');
497 }
498 }
499
500 // Export playlist
501 async function exportPlaylist() {
502 const videoElements = document.querySelectorAll('ytd-playlist-video-renderer a#video-title');
503 if (videoElements.length === 0) {
504 showNotification('No playlist found');
505 return;
506 }
507
508 const videos = Array.from(videoElements).map(el => ({
509 title: el.textContent.trim(),
510 url: 'https://www.youtube.com' + el.getAttribute('href')
511 }));
512
513 const text = videos.map(v => `${v.title}\n${v.url}`).join('\n\n');
514 await GM.setClipboard(text);
515 showNotification(`Exported ${videos.length} videos to clipboard`);
516 }
517
518 // Hide shorts
519 function hideShorts() {
520 const style = document.createElement('style');
521 style.id = 'yt-hide-shorts-style';
522 style.textContent = `
523 ytd-reel-shelf-renderer,
524 ytd-rich-shelf-renderer[is-shorts],
525 ytd-guide-entry-renderer:has(a[href="/shorts"]),
526 ytd-mini-guide-entry-renderer:has(a[href="/shorts/"]) {
527 display: none !important;
528 }
529 `;
530 document.head.appendChild(style);
531 }
532
533 // Show shorts
534 function showShorts() {
535 const style = document.getElementById('yt-hide-shorts-style');
536 if (style) {
537 style.remove();
538 }
539 }
540
541 // Make panel draggable
542 function makeDraggable() {
543 const panel = document.getElementById('yt-automation-panel');
544 const header = document.getElementById('yt-automation-header');
545 let isDragging = false;
546 let currentX;
547 let currentY;
548 let initialX;
549 let initialY;
550
551 header.addEventListener('mousedown', (e) => {
552 isDragging = true;
553 initialX = e.clientX - panel.offsetLeft;
554 initialY = e.clientY - panel.offsetTop;
555 });
556
557 document.addEventListener('mousemove', (e) => {
558 if (isDragging) {
559 e.preventDefault();
560 currentX = e.clientX - initialX;
561 currentY = e.clientY - initialY;
562
563 panel.style.left = currentX + 'px';
564 panel.style.top = currentY + 'px';
565 panel.style.right = 'auto';
566 }
567 });
568
569 document.addEventListener('mouseup', () => {
570 isDragging = false;
571 });
572 }
573
574 // Monitor video end for auto-pause
575 function monitorVideoEnd() {
576 const video = getVideoElement();
577 if (!video) return;
578
579 video.addEventListener('ended', async () => {
580 const autoPauseEnd = await GM.getValue('autoPauseEnd', false);
581 if (autoPauseEnd) {
582 showNotification('Video ended - Auto-pause active');
583 }
584 });
585 }
586
587 // Initialize
588 async function init() {
589 console.log('Initializing YouTube Automation Toolkit...');
590
591 // Wait for page to be ready
592 if (document.readyState === 'loading') {
593 document.addEventListener('DOMContentLoaded', init);
594 return;
595 }
596
597 // Wait a bit for YouTube to load
598 await new Promise(resolve => setTimeout(resolve, 2000));
599
600 createControlPanel();
601 monitorVideoEnd();
602
603 console.log('YouTube Automation Toolkit ready!');
604 }
605
606 // Start the extension
607 init();
608})();