Video Screenshot Tool

Take screenshots from videos with a floating button

Size

8.4 KB

Version

1.1.1

Created

Oct 18, 2025

Updated

5 days ago

1// ==UserScript==
2// @name		Video Screenshot Tool
3// @description		Take screenshots from videos with a floating button
4// @version		1.1.1
5// @match		https://*.anime1.me/*
6// @icon		https://anime1.me/favicon-32x32.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Video Screenshot Tool initialized');
12
13    // Add styles for the screenshot button
14    TM_addStyle(`
15        .video-screenshot-btn {
16            position: absolute;
17            top: 10px;
18            right: 10px;
19            width: 36px;
20            height: 36px;
21            background: rgba(0, 0, 0, 0.7);
22            border: 2px solid rgba(255, 255, 255, 0.8);
23            border-radius: 6px;
24            cursor: pointer;
25            display: flex;
26            align-items: center;
27            justify-content: center;
28            z-index: 9999;
29            opacity: 0;
30            transition: opacity 0.3s ease, background 0.2s ease;
31            pointer-events: none;
32        }
33
34        .video-screenshot-btn.visible {
35            opacity: 1;
36            pointer-events: auto;
37        }
38
39        .video-screenshot-btn:hover {
40            background: rgba(0, 0, 0, 0.9);
41            border-color: rgba(255, 255, 255, 1);
42        }
43
44        .video-screenshot-btn:active {
45            transform: scale(0.95);
46        }
47
48        .video-screenshot-btn svg {
49            width: 20px;
50            height: 20px;
51            fill: white;
52        }
53
54        .video-screenshot-notification {
55            position: fixed;
56            top: 20px;
57            right: 20px;
58            background: rgba(0, 0, 0, 0.9);
59            color: white;
60            padding: 12px 20px;
61            border-radius: 6px;
62            z-index: 10000;
63            font-size: 14px;
64            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
65            animation: slideIn 0.3s ease;
66        }
67
68        @keyframes slideIn {
69            from {
70                transform: translateX(100%);
71                opacity: 0;
72            }
73            to {
74                transform: translateX(0);
75                opacity: 1;
76            }
77        }
78    `);
79
80    // Function to create screenshot button
81    function createScreenshotButton() {
82        const button = document.createElement('div');
83        button.className = 'video-screenshot-btn';
84        button.title = 'Take Screenshot';
85        
86        // Camera icon SVG
87        button.innerHTML = `
88            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
89                <path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
90                <circle cx="12" cy="12" r="3"/>
91            </svg>
92        `;
93        
94        return button;
95    }
96
97    // Function to show notification
98    function showNotification(message) {
99        const notification = document.createElement('div');
100        notification.className = 'video-screenshot-notification';
101        notification.textContent = message;
102        document.body.appendChild(notification);
103        
104        setTimeout(() => {
105            notification.style.animation = 'slideIn 0.3s ease reverse';
106            setTimeout(() => notification.remove(), 300);
107        }, 2000);
108    }
109
110    // Function to take screenshot
111    function takeScreenshot(videoElement) {
112        try {
113            console.log('Taking screenshot from video:', videoElement);
114            
115            // Set crossOrigin to allow canvas capture from cross-origin videos
116            if (!videoElement.crossOrigin) {
117                videoElement.crossOrigin = 'anonymous';
118                console.log('Set crossOrigin to anonymous');
119            }
120            
121            // Create canvas with video dimensions
122            const canvas = document.createElement('canvas');
123            canvas.width = videoElement.videoWidth;
124            canvas.height = videoElement.videoHeight;
125            
126            // Check if video has valid dimensions
127            if (canvas.width === 0 || canvas.height === 0) {
128                showNotification('Video not ready. Please try again.');
129                console.error('Video dimensions are zero');
130                return;
131            }
132            
133            // Draw video frame to canvas
134            const ctx = canvas.getContext('2d');
135            ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
136            
137            // Convert to JPEG and download
138            canvas.toBlob((blob) => {
139                if (blob) {
140                    const url = URL.createObjectURL(blob);
141                    const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
142                    const filename = `video-screenshot-${timestamp}.jpg`;
143                    
144                    const a = document.createElement('a');
145                    a.href = url;
146                    a.download = filename;
147                    a.click();
148                    
149                    URL.revokeObjectURL(url);
150                    showNotification('Screenshot saved!');
151                    console.log('Screenshot saved as:', filename);
152                } else {
153                    showNotification('Failed to create screenshot');
154                    console.error('Failed to create blob from canvas');
155                }
156            }, 'image/jpeg', 0.95);
157            
158        } catch (error) {
159            console.error('Error taking screenshot:', error);
160            showNotification('Error taking screenshot: ' + error.message);
161        }
162    }
163
164    // Function to setup screenshot button for a video container
165    function setupScreenshotButton(videoContainer) {
166        // Check if button already exists
167        if (videoContainer.querySelector('.video-screenshot-btn')) {
168            console.log('Screenshot button already exists for this container');
169            return;
170        }
171
172        // Find the video element
173        const videoElement = videoContainer.querySelector('video');
174        if (!videoElement) {
175            console.log('No video element found in container');
176            return;
177        }
178
179        console.log('Setting up screenshot button for video container');
180
181        // Create and add button
182        const button = createScreenshotButton();
183        videoContainer.style.position = 'relative';
184        videoContainer.appendChild(button);
185
186        // Show button on hover
187        let hoverTimeout;
188        videoContainer.addEventListener('mouseenter', () => {
189            clearTimeout(hoverTimeout);
190            button.classList.add('visible');
191        });
192
193        videoContainer.addEventListener('mouseleave', () => {
194            hoverTimeout = setTimeout(() => {
195                button.classList.remove('visible');
196            }, 300);
197        });
198
199        // Take screenshot on click
200        button.addEventListener('click', (e) => {
201            e.stopPropagation();
202            takeScreenshot(videoElement);
203        });
204
205        console.log('Screenshot button setup complete');
206    }
207
208    // Function to find and setup all video containers
209    function findAndSetupVideos() {
210        // Look for Video.js containers
211        const videoContainers = document.querySelectorAll('.video-js, .vjscontainer, video');
212        
213        console.log(`Found ${videoContainers.length} potential video container(s)`);
214        
215        videoContainers.forEach(container => {
216            // If it's a video element directly, find its parent container
217            if (container.tagName === 'VIDEO') {
218                const parent = container.closest('.video-js, .vjscontainer');
219                if (parent) {
220                    setupScreenshotButton(parent);
221                }
222            } else {
223                setupScreenshotButton(container);
224            }
225        });
226    }
227
228    // Initialize when DOM is ready
229    function init() {
230        console.log('Initializing Video Screenshot Tool');
231        
232        // Initial setup
233        findAndSetupVideos();
234        
235        // Watch for dynamically added videos
236        const observer = new MutationObserver((mutations) => {
237            for (const mutation of mutations) {
238                if (mutation.addedNodes.length) {
239                    findAndSetupVideos();
240                }
241            }
242        });
243        
244        observer.observe(document.body, {
245            childList: true,
246            subtree: true
247        });
248        
249        console.log('Video Screenshot Tool ready');
250    }
251
252    // Run when body is ready
253    if (document.body) {
254        init();
255    } else {
256        TM_runBody(init);
257    }
258
259})();
Video Screenshot Tool | Robomonkey