YouTube Video Control Suite

Hide controls until hover, disable autoplay/captions, and add screenshot button

Size

9.2 KB

Version

1.2.2

Created

Oct 18, 2025

Updated

11 days ago

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