Scrimba Caption Downloader

Download video captions as clean text with one click

Size

8.4 KB

Version

1.0.1

Created

Oct 29, 2025

Updated

17 days ago

1// ==UserScript==
2// @name		Scrimba Caption Downloader
3// @description		Download video captions as clean text with one click
4// @version		1.0.1
5// @match		https://*.scrimba.com/*
6// @icon		https://scrimba.com/static/brand/favicon-32x32.png
7// @grant		GM.setClipboard
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('Scrimba Caption Downloader: Extension loaded');
13
14    // Function to extract clean text from captions
15    function extractCleanCaptions() {
16        const captionsElement = document.querySelector('ide-clip-captions');
17        
18        if (!captionsElement) {
19            console.error('Captions element not found');
20            return null;
21        }
22
23        // Get all span elements within the captions
24        const spans = captionsElement.querySelectorAll('span');
25        
26        if (spans.length === 0) {
27            console.error('No caption spans found');
28            return null;
29        }
30
31        // Extract text from all spans and join them
32        let cleanText = '';
33        spans.forEach(span => {
34            const text = span.textContent.trim();
35            if (text && text !== ' ') {
36                cleanText += text + ' ';
37            }
38        });
39
40        // Clean up extra spaces
41        cleanText = cleanText.replace(/\s+/g, ' ').trim();
42        
43        console.log('Extracted captions length:', cleanText.length);
44        return cleanText;
45    }
46
47    // Function to download captions as text file
48    function downloadCaptions() {
49        const captions = extractCleanCaptions();
50        
51        if (!captions) {
52            alert('Could not extract captions. Please make sure the video has loaded.');
53            return;
54        }
55
56        // Create a blob with the text content
57        const blob = new Blob([captions], { type: 'text/plain' });
58        const url = URL.createObjectURL(blob);
59        
60        // Create a temporary download link
61        const a = document.createElement('a');
62        a.href = url;
63        a.download = `scrimba-captions-${Date.now()}.txt`;
64        document.body.appendChild(a);
65        a.click();
66        
67        // Cleanup
68        document.body.removeChild(a);
69        URL.revokeObjectURL(url);
70        
71        console.log('Captions downloaded successfully');
72    }
73
74    // Function to copy captions to clipboard
75    async function copyCaptions() {
76        const captions = extractCleanCaptions();
77        
78        if (!captions) {
79            alert('Could not extract captions. Please make sure the video has loaded.');
80            return;
81        }
82
83        try {
84            await GM.setClipboard(captions);
85            showNotification('Captions copied to clipboard!');
86            console.log('Captions copied to clipboard');
87        } catch (error) {
88            console.error('Failed to copy to clipboard:', error);
89            alert('Failed to copy to clipboard');
90        }
91    }
92
93    // Function to show notification
94    function showNotification(message) {
95        const notification = document.createElement('div');
96        notification.textContent = message;
97        notification.style.cssText = `
98            position: fixed;
99            top: 20px;
100            right: 20px;
101            background: #4CAF50;
102            color: white;
103            padding: 15px 20px;
104            border-radius: 8px;
105            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
106            z-index: 10000;
107            font-family: Arial, sans-serif;
108            font-size: 14px;
109            animation: slideIn 0.3s ease-out;
110        `;
111        
112        document.body.appendChild(notification);
113        
114        setTimeout(() => {
115            notification.style.animation = 'slideOut 0.3s ease-out';
116            setTimeout(() => {
117                document.body.removeChild(notification);
118            }, 300);
119        }, 2000);
120    }
121
122    // Add CSS for animations
123    const style = document.createElement('style');
124    style.textContent = `
125        @keyframes slideIn {
126            from {
127                transform: translateX(400px);
128                opacity: 0;
129            }
130            to {
131                transform: translateX(0);
132                opacity: 1;
133            }
134        }
135        @keyframes slideOut {
136            from {
137                transform: translateX(0);
138                opacity: 1;
139            }
140            to {
141                transform: translateX(400px);
142                opacity: 0;
143            }
144        }
145    `;
146    document.head.appendChild(style);
147
148    // Function to create and add the download button
149    function addDownloadButton() {
150        const captionsElement = document.querySelector('ide-clip-captions');
151        
152        if (!captionsElement) {
153            console.log('Captions element not found yet, will retry...');
154            return false;
155        }
156
157        // Check if button already exists
158        if (document.getElementById('caption-download-btn')) {
159            return true;
160        }
161
162        // Create button container
163        const buttonContainer = document.createElement('div');
164        buttonContainer.id = 'caption-download-btn';
165        buttonContainer.style.cssText = `
166            position: fixed;
167            bottom: 20px;
168            right: 20px;
169            display: flex;
170            gap: 10px;
171            z-index: 9999;
172        `;
173
174        // Create download button
175        const downloadBtn = document.createElement('button');
176        downloadBtn.textContent = '📥 Download Captions';
177        downloadBtn.style.cssText = `
178            background: #2196F3;
179            color: white;
180            border: none;
181            padding: 12px 20px;
182            border-radius: 8px;
183            cursor: pointer;
184            font-size: 14px;
185            font-weight: 600;
186            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
187            transition: all 0.3s ease;
188            font-family: Arial, sans-serif;
189        `;
190        
191        downloadBtn.onmouseover = () => {
192            downloadBtn.style.background = '#1976D2';
193            downloadBtn.style.transform = 'translateY(-2px)';
194            downloadBtn.style.boxShadow = '0 6px 8px rgba(0,0,0,0.3)';
195        };
196        
197        downloadBtn.onmouseout = () => {
198            downloadBtn.style.background = '#2196F3';
199            downloadBtn.style.transform = 'translateY(0)';
200            downloadBtn.style.boxShadow = '0 4px 6px rgba(0,0,0,0.2)';
201        };
202        
203        downloadBtn.onclick = downloadCaptions;
204
205        // Create copy button
206        const copyBtn = document.createElement('button');
207        copyBtn.textContent = '📋 Copy';
208        copyBtn.style.cssText = `
209            background: #FF9800;
210            color: white;
211            border: none;
212            padding: 12px 20px;
213            border-radius: 8px;
214            cursor: pointer;
215            font-size: 14px;
216            font-weight: 600;
217            box-shadow: 0 4px 6px rgba(0,0,0,0.2);
218            transition: all 0.3s ease;
219            font-family: Arial, sans-serif;
220        `;
221        
222        copyBtn.onmouseover = () => {
223            copyBtn.style.background = '#F57C00';
224            copyBtn.style.transform = 'translateY(-2px)';
225            copyBtn.style.boxShadow = '0 6px 8px rgba(0,0,0,0.3)';
226        };
227        
228        copyBtn.onmouseout = () => {
229            copyBtn.style.background = '#FF9800';
230            copyBtn.style.transform = 'translateY(0)';
231            copyBtn.style.boxShadow = '0 4px 6px rgba(0,0,0,0.2)';
232        };
233        
234        copyBtn.onclick = copyCaptions;
235
236        buttonContainer.appendChild(downloadBtn);
237        buttonContainer.appendChild(copyBtn);
238        document.body.appendChild(buttonContainer);
239        
240        console.log('Download buttons added successfully');
241        return true;
242    }
243
244    // Initialize the extension
245    function init() {
246        console.log('Initializing Scrimba Caption Downloader...');
247        
248        // Try to add button immediately
249        if (addDownloadButton()) {
250            return;
251        }
252
253        // If not found, wait for the page to load and try again
254        const observer = new MutationObserver(() => {
255            if (addDownloadButton()) {
256                observer.disconnect();
257            }
258        });
259
260        observer.observe(document.body, {
261            childList: true,
262            subtree: true
263        });
264
265        // Also try after a delay as fallback
266        setTimeout(() => {
267            addDownloadButton();
268        }, 2000);
269    }
270
271    // Start when DOM is ready
272    if (document.readyState === 'loading') {
273        document.addEventListener('DOMContentLoaded', init);
274    } else {
275        init();
276    }
277})();
Scrimba Caption Downloader | Robomonkey