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})();