YouTube Video Control Suite

Auto-pause on load, hide controls until hover, disable autoplay/captions, and add screenshot button

Size

8.8 KB

Version

1.0.1

Created

Oct 18, 2025

Updated

3 days ago

1// ==UserScript==
2// @name		YouTube Video Control Suite
3// @description		Auto-pause on load, hide controls until hover, disable autoplay/captions, and add screenshot button
4// @version		1.0.1
5// @match		https://*.youtube.com/*
6// @icon		https://www.youtube.com/s/desktop/a192c735/img/favicon_32x32.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('YouTube Video Control Suite initialized');
12
13    let videoPausedOnce = false;
14    let controlsHidden = false;
15    let screenshotButton = null;
16
17    // Debounce function for performance
18    function debounce(func, wait) {
19        let timeout;
20        return function executedFunction(...args) {
21            const later = () => {
22                clearTimeout(timeout);
23                func(...args);
24            };
25            clearTimeout(timeout);
26            timeout = setTimeout(later, wait);
27        };
28    }
29
30    // Feature 1: Pause video on page load (one time only)
31    function pauseVideoOnLoad(video) {
32        if (!videoPausedOnce && !video.paused) {
33            console.log('Pausing video on initial load');
34            video.pause();
35            videoPausedOnce = true;
36        }
37    }
38
39    // Feature 2: Hide video controls, show only on control bar hover
40    function setupControlsVisibility(video) {
41        if (controlsHidden) return;
42
43        const player = document.querySelector('.html5-video-player');
44        if (!player) return;
45
46        const controlsBar = document.querySelector('.ytp-chrome-bottom');
47        if (!controlsBar) return;
48
49        console.log('Setting up controls visibility');
50
51        // Add custom styles to hide controls by default
52        TM_addStyle(`
53            .html5-video-player .ytp-chrome-bottom {
54                opacity: 0 !important;
55                transition: opacity 0.3s ease !important;
56            }
57            
58            .html5-video-player .ytp-chrome-bottom:hover {
59                opacity: 1 !important;
60            }
61            
62            .html5-video-player.ytp-autohide .ytp-chrome-bottom:hover {
63                opacity: 1 !important;
64            }
65        `);
66
67        controlsHidden = true;
68    }
69
70    // Feature 3: Disable autoplay next
71    function disableAutoplayNext() {
72        const autoplayToggle = document.querySelector('.ytp-autonav-toggle-button');
73        if (autoplayToggle) {
74            const isAutoplayOn = autoplayToggle.getAttribute('aria-checked') === 'true';
75            if (isAutoplayOn) {
76                console.log('Disabling autoplay next');
77                autoplayToggle.click();
78            }
79        }
80    }
81
82    // Feature 4: Disable subtitles/closed captions by default
83    function disableSubtitles(video) {
84        const player = document.querySelector('.html5-video-player');
85        if (!player) return;
86
87        const subtitlesButton = document.querySelector('.ytp-subtitles-button');
88        if (subtitlesButton) {
89            const isSubtitlesOn = subtitlesButton.getAttribute('aria-pressed') === 'true';
90            if (isSubtitlesOn) {
91                console.log('Disabling subtitles');
92                subtitlesButton.click();
93            }
94        }
95
96        // Also disable text tracks directly
97        if (video.textTracks) {
98            for (let i = 0; i < video.textTracks.length; i++) {
99                if (video.textTracks[i].mode === 'showing') {
100                    video.textTracks[i].mode = 'disabled';
101                }
102            }
103        }
104    }
105
106    // Feature 5: Add screenshot button
107    function createScreenshotButton(video) {
108        if (screenshotButton) return;
109
110        const player = document.querySelector('.html5-video-player');
111        if (!player) return;
112
113        console.log('Creating screenshot button');
114
115        // Create button
116        screenshotButton = document.createElement('button');
117        screenshotButton.id = 'yt-screenshot-btn';
118        screenshotButton.innerHTML = '📷';
119        screenshotButton.title = 'Take Screenshot';
120        
121        // Style the button
122        screenshotButton.style.cssText = `
123            position: absolute;
124            top: 10px;
125            right: 10px;
126            width: 36px;
127            height: 36px;
128            background: rgba(0, 0, 0, 0.7);
129            border: 2px solid rgba(255, 255, 255, 0.3);
130            border-radius: 4px;
131            color: white;
132            font-size: 18px;
133            cursor: pointer;
134            z-index: 9999;
135            opacity: 0;
136            transition: opacity 0.3s ease, background 0.2s ease;
137            display: flex;
138            align-items: center;
139            justify-content: center;
140            padding: 0;
141        `;
142
143        // Show button on hover
144        player.addEventListener('mouseenter', () => {
145            screenshotButton.style.opacity = '1';
146        });
147
148        player.addEventListener('mouseleave', () => {
149            screenshotButton.style.opacity = '0';
150        });
151
152        // Hover effect on button
153        screenshotButton.addEventListener('mouseenter', () => {
154            screenshotButton.style.background = 'rgba(0, 0, 0, 0.9)';
155            screenshotButton.style.borderColor = 'rgba(255, 255, 255, 0.6)';
156        });
157
158        screenshotButton.addEventListener('mouseleave', () => {
159            screenshotButton.style.background = 'rgba(0, 0, 0, 0.7)';
160            screenshotButton.style.borderColor = 'rgba(255, 255, 255, 0.3)';
161        });
162
163        // Screenshot functionality
164        screenshotButton.addEventListener('click', () => {
165            console.log('Taking screenshot');
166            
167            const canvas = document.createElement('canvas');
168            canvas.width = video.videoWidth;
169            canvas.height = video.videoHeight;
170            
171            const ctx = canvas.getContext('2d');
172            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
173            
174            canvas.toBlob((blob) => {
175                const url = URL.createObjectURL(blob);
176                const a = document.createElement('a');
177                a.href = url;
178                
179                // Generate filename with video title and timestamp
180                const videoTitle = document.querySelector('h1.ytd-watch-metadata yt-formatted-string')?.textContent || 'youtube-video';
181                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
182                const currentTime = Math.floor(video.currentTime);
183                
184                a.download = `${videoTitle.slice(0, 50)}_${currentTime}s_${timestamp}.png`;
185                a.click();
186                
187                URL.revokeObjectURL(url);
188                
189                // Visual feedback
190                screenshotButton.innerHTML = '✓';
191                setTimeout(() => {
192                    screenshotButton.innerHTML = '📷';
193                }, 1000);
194            });
195        });
196
197        player.appendChild(screenshotButton);
198    }
199
200    // Main initialization function
201    function initializeFeatures() {
202        const video = document.querySelector('video.html5-main-video');
203        if (!video) return;
204
205        console.log('Video element found, initializing features');
206
207        // Feature 1: Pause on load
208        pauseVideoOnLoad(video);
209
210        // Feature 2: Setup controls visibility
211        setupControlsVisibility(video);
212
213        // Feature 3: Disable autoplay (check periodically as it might load later)
214        disableAutoplayNext();
215        setTimeout(disableAutoplayNext, 2000);
216        setTimeout(disableAutoplayNext, 5000);
217
218        // Feature 4: Disable subtitles
219        disableSubtitles(video);
220        setTimeout(() => disableSubtitles(video), 1000);
221
222        // Feature 5: Create screenshot button
223        createScreenshotButton(video);
224    }
225
226    // Watch for video element and player changes
227    const observer = new MutationObserver(debounce(() => {
228        initializeFeatures();
229    }, 500));
230
231    // Start observing when body is ready
232    function startObserving() {
233        if (document.body) {
234            observer.observe(document.body, {
235                childList: true,
236                subtree: true
237            });
238            initializeFeatures();
239        } else {
240            setTimeout(startObserving, 100);
241        }
242    }
243
244    // Handle YouTube's SPA navigation
245    let lastUrl = location.href;
246    new MutationObserver(() => {
247        const currentUrl = location.href;
248        if (currentUrl !== lastUrl) {
249            console.log('URL changed, reinitializing features');
250            lastUrl = currentUrl;
251            videoPausedOnce = false;
252            controlsHidden = false;
253            screenshotButton = null;
254            setTimeout(initializeFeatures, 1000);
255        }
256    }).observe(document.querySelector('title'), { childList: true, subtree: true });
257
258    // Initialize
259    if (document.readyState === 'loading') {
260        document.addEventListener('DOMContentLoaded', startObserving);
261    } else {
262        startObserving();
263    }
264
265    console.log('YouTube Video Control Suite loaded');
266})();
YouTube Video Control Suite | Robomonkey