YouTube Automation Toolkit

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})();
YouTube Automation Toolkit | Robomonkey